如何在 JavaScript 中添加延迟

3
function abc(elm){
    this.$elm =  document.querySelector(elm)
}

abc.prototype.addClass =  function (str){
  this.$elm.classList.add(str)
    return this
}

abc.prototype.removeClass =  function (str){
   this.$elm.classList.remove(str)
    return this
}

abc.prototype.delay =  function (timer){
   let self = this
  
  setTimeout(()=>{
    return self
  },timer)
    return this
}

function $(str){
  return new abc(str);
}

let x = $('#test').delay(5000).delay(1000).addClass('red');

console.log($('#test'));

我想在6秒后添加red类。我尝试使用setTimeout,但没有起作用。你能否提供更好的建议?

我想编写一个延迟函数,在继续执行下一段代码之前暂停一段时间。


你是在尝试重新实现jQuery吗?如果是,为什么?阅读jQuery源代码是否能解决你的问题? - Lionel Rowe
这个回答解决了你的问题吗?如何在JavaScript中设置时间延迟 - Liam
我不想使用 async await,我能使用 Promise。 - user944513
4个回答

3
你可以基于 Promise 创建一个非常简单的任务队列。由于 Promise 执行本身就使用了一个任务队列,你只需要保留一个 Promise 并在添加新任务时通过 .then() 进行链式调用,并保持最新的 Promise。这样,如果你添加了三个任务 T1 -> T2 -> T3,它们将按照添加的顺序排队执行。如果你添加了一个只是在它们之间添加简单延迟的任务,比如 T1 -> 等待 6 秒钟 -> T2 -> 等待 5 秒钟 -> T3,那么任务也会被分散执行。
以下是一个样例实现,用于说明这个想法,它利用了 thunks(不接受参数的函数)作为延迟执行的任务。

function abc(elm){
    this.$elm =  document.querySelector(elm)
    this.queue = Promise.resolve();
}

/**
 * Uniform way of adding a task for later execution
 * @param {Function} task - a thunk to be executed later
 * @param {number} [delay=0] time in milliseconds to wait after last task finished before executing this on
 */
abc.prototype.addTask = function(task, delay = 0) {
  const waitFor = () => new Promise( res => setTimeout(res, delay) );
  
  this.queue = this.queue
        .then(waitFor)
        .then(task)
}

abc.prototype.addClass =  function (str){
  this.addTask(() => this.$elm.classList.add(str));
  return this
}

abc.prototype.removeClass =  function (str){
  this.addTask(() => this.$elm.classList.remove(str));
  return this
}

abc.prototype.delay =  function (timer){
  // add an empty function as a task. If needed this can also do logging or other internal logic
  this.addTask(() => {}, timer);
  return this
}

function $(str){
  return new abc(str);
}

//usage

let x = $('#test').delay(5000).delay(1000).addClass('red');

x.delay(1000)
  .delay(1000)
  .delay(1000)
  .delay(1000)
  .delay(1000) //5 seconds
  .removeClass('red');
.red {
  background-color: red;
  color: white;
}
<p id="test">
Bacon ipsum dolor amet hamburger t-bone pork, pastrami sirloin swine corned beef tenderloin frankfurter tail ball tip meatball pork belly spare ribs prosciutto. Bresaola turkey buffalo jowl t-bone biltong burgdoggen cow capicola meatball pastrami boudin alcatra. Bresaola chicken bacon cow, frankfurter meatball hamburger jerky. Shankle capicola chicken leberkas turkey. Ball tip bacon doner kielbasa jerky. Salami picanha chicken bacon, turducken buffalo chislic andouille porchetta tongue shankle prosciutto t-bone. Beef andouille cow pork chop alcatra, turducken ribeye sirloin tail boudin strip steak doner.
</p>


1
你需要承诺。
abc.prototype.delay = function (timer) {
    return new Promise((resolve) => {
        let self = this
        setTimeout(() => {
            return resolve(self)
        }, timer)
        return resolve(this);
    })

}
let x = $('#test').delay(5000);

请看下面的例子。

function abc(elm) {
  this.$elm = document.querySelector(elm)
}

abc.prototype.addClass = function(str) {
  this.$elm.classList.add(str)
  return this
}

abc.prototype.removeClass = function(str) {
  this.$elm.classList.remove(str)
  return this
}

abc.prototype.delay = function(timer) {
  return new Promise((resolve) => {
    let self = this
    setTimeout(() => {
      return resolve(self)
    }, timer)
  })
}

function $(str) {
  return new abc(str);
}


async function hello() {
  let x = $('body')
  await x.delay(5000);
  x.addClass('red');
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<style>
  .red {
    background: red;
  }
</style>

<body>
  <button onclick="hello()">Click here...</button>
</body>

</html>


1
使用 Promise 是可以的,但是 OP 就不能像在他们的示例代码中那样直接链接另一个 delay/addClass - TiiJ7

0

你不能用那种方式延迟执行。

你原来的延迟函数确实创建了一个超时回调,但它在超时发生之前立即返回自身。

以你的示例为例:

$('#test').delay(5000).delay(1000).addClass('red');

以下是发生的事情:

  1. 调用 $
  2. 在 #1 的结果上调用 delay
  3. 在 #2 的结果上调用 delay
  4. 在 #3 的结果上调用 addClass
  5. #3 中设置的超时执行,没有人关心返回的结果
  6. #2 中设置的超时执行,没有人关心返回的结果

要获得所需的结果,您可以简单地累积所需的延迟,并在操作点应用它,如下所示:

function abc(elm){
    this.$elm =  document.querySelector(elm);
    this._delayTimeout = 0;
    this._delayExec = (cb) => {
      window.setTimeout(cb, this._delayTimeout);
    }
}

abc.prototype.addClass =  function (str){
  
  this._delayExec(() => {
    this.$elm.classList.add(str)
  });
  
  return this;
  
}

abc.prototype.removeClass =  function (str){
   
  this._delayExec(() => {
    this.$elm.classList.remove(str)
  });
   
  return this;
  
}

abc.prototype.delay =  function (timer) {
  this._delayTimeout += timer;
  console.log(this._delayTimeout);
  return this;
}

function $(str){
  return new abc(str);
}

let x = $('#test').delay(1000).addClass('red').delay(2000).removeClass('red').addClass('green');

console.log($('#test'));
.red {
  color: red;
}

.green {
  background-color: green;
}
<div id="test">Test</div>


在这种情况下,这将失败 $('#test').delay(1000).addClass('red').delay(5000).removeClass('red').addClass('red') - user944513
预期输出是 在1秒后添加类名red并等待5秒,然后移除类名并添加类名red而不等待 - user944513

0
你不能通过同步的方式来做那件事。那会导致整个应用程序在超时期间停止。
最好的方法是使用异步回调函数
abc.prototype.delay = function (timer, callback){
    let self = this;
    setTimeout(() => {
        callback(self);
    }, timer);
}
$('#test').delay(5000, function(element) {
    $(element).delay(5000, function(element) {
        console.log("Here we are!");
    });
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接