Javascript 你如何在承诺中包装 setTimeout

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33843091/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 15:33:01  来源:igfitidea点击:

How do you wrap setTimeout in a promise

javascriptpromise

提问by akc42

I am trying to run a test suite for an object that returns a promise. I want to chain several actions together with short timeouts between them. I thought that a "then" call which returned a promise would wait for the promise to be fulfilled before firing the next chained then call.

我正在尝试为返回承诺的对象运行测试套件。我想将几个动作链接在一起,并在它们之间设置短暂的超时时间。我认为返回承诺的“then”调用将等待承诺完成,然后再触发下一个链接的 then 调用。

I created a function

我创建了一个函数

function promiseTimeout (time) {
  return new Promise(function(resolve,reject){
    setTimeout(function(){resolve(time);},time);
  });
};

to try and wrap setTimeout in a Promise.

尝试将 setTimeout 包装在 Promise 中。

Then in my test suite, I am calling something like this ...

然后在我的测试套件中,我正在调用这样的东西......

    it('should restore state when browser back button is used',function(done){
      r.domOK().then(function(){
        xh.fire('akc-route-change','/user/4/profile/new');
      }).then(promiseTimeout(2000)).then(function(t){
        xu.fire('akc-route-change','/user/6');
      }).then(promiseTimeout(10)).then(function(t){
        expect(xu.params[0]).to.equal(6);
        history.back();
      }).then(promiseTimeout(10)).then(function(){
        expect(xu.params[0]).to.equal(4);
        done();
      });
    });

I can put a breakpoint on the first xh.firecall and a second one on the xu.firecall and would have expected a two second gap when a continues from the first breakpoint to the second.

我可以在第一个xh.fire调用上设置断点,在调用上设置第二个断点,并且xu.fire当 a 从第一个断点继续到第二个断点时,预计会有两秒的间隔。

Instead it reaches the second breakpoint immediately, and the value of tat that point is undefined.

相反,它立即到达第二个断点,并且t在该点处的值未定义。

What am I doing wrong?

我究竟做错了什么?

采纳答案by Jaromanda X

TL;DR - you've wrapped setTimeout in a promise properly, the issue is you are using it improperly

TL; DR - 您已将 setTimeout 正确包装在承诺中,问题是您使用不当

.then(promiseTimeout(2000)).then

will not do what you expect. The "signature" for .then is then(functionResolved, functionRejected)

不会做你期望的。.then 的“签名”是then(functionResolved, functionRejected)

A promise's then method accepts two arguments:

promise.then(onFulfilled, onRejected)

Both onFulfilled and onRejected are optional arguments:

  • If onFulfilled is not a function, it must be ignored.
  • If onRejected is not a function, it must be ignored.

一个 promise 的 then 方法接受两个参数:

promise.then(onFulfilled, onRejected)

onFulfilled 和 onRejected 都是可选参数:

  • 如果 onFulfilled 不是函数,则必须忽略它。
  • 如果 onRejected 不是函数,则必须忽略它。

source: https://promisesaplus.com/#point-21

来源:https: //promisesaplus.com/#point-21

You are not passing a function to then

您没有将函数传递给 then

Consider the way you are doing it:

考虑你的做法:

Promise.resolve('hello')
.then(promiseTimeout(2000))
.then(console.log.bind(console))

vs how it should be done:

vs应该如何完成:

Promise.resolve('hello').then(function() { 
    return promiseTimeout(2000)
}).then(console.log.bind(console))

The first outputs 'hello' immediately

第一个立即输出 'hello'

The second outputs 2000 after 2 seconds

2秒后第二个输出2000

Therefore, you should be doing:

因此,你应该这样做:

it('should restore state when browser back button is used', function(done) {
    r.domOK().then(function() {
        xh.fire('akc-route-change', '/user/4/profile/new');
    }).then(function() {
        return promiseTimeout(2000);
    }).then(function(t) {
        xu.fire('akc-route-change', '/user/6');
    }).then(function() {
        return promiseTimeout(10);
    }).then(function(t) {
        expect(xu.params[0]).to.equal(6);
        history.back();
    }).then(function() {
        return promiseTimeout(10);
    }).then(function() {
        expect(xu.params[0]).to.equal(4);
        done();
    });
});

Alternatively:

或者:

it('should restore state when browser back button is used', function(done) {
    r.domOK().then(function() {
        xh.fire('akc-route-change', '/user/4/profile/new');
    }).then(promiseTimeout.bind(null, 2000)
    ).then(function(t) {
        xu.fire('akc-route-change', '/user/6');
    }).then(promiseTimeout.bind(null, 10)
    ).then(function(t) {
        expect(xu.params[0]).to.equal(6);
        history.back();
    }).then(promiseTimeout.bind(null, 10)
    ).then(function() {
        expect(xu.params[0]).to.equal(4);
        done();
    });
});

EDIT: March 2019

编辑:2019 年 3 月

Over the years, things have changed a lot - arrow notation makes this even easier

多年来,事情发生了很大变化 - 箭头符号使这变得更加容易

Firstly, I would define promiseTimeout differently

首先,我会以不同的方式定义 promiseTimeout

const promiseTimeout = time => () => new Promise(resolve => setTimeout(resolve, time, time));

The above returns a function that can be called to create a "promise delay" and resolves to the time (length of delay). Thinking about this, I can't see why that would be very useful, rather I'd:

以上返回一个函数,可以调用该函数来创建“承诺延迟”并解析为时间(延迟长度)。考虑到这一点,我不明白为什么这会非常有用,而是我会:

const promiseTimeout = time => result => new Promise(resolve => setTimeout(resolve, time, result));

The above would resolve to the result of the previous promise (far more useful)

以上将解决上一个承诺的结果(更有用)

But it's a function that returns a function, so the rest of the ORIGINAL code could be left unchanged. The thing about the original code, however, is that no values are needed to be passed down the .then chain, so, even simpler

但它是一个返回函数的函数,所以其余的原始代码可以保持不变。然而,关于原始代码的事情是不需要值向下传递到 .then 链,所以,甚至更简单

const promiseTimeout = time => () => new Promise(resolve => setTimeout(resolve, time));

and the original code in the question's itblock can now be used unchanged

并且问题it块中的原始代码现在可以不变地使用

it('should restore state when browser back button is used',function(done){
  r.domOK().then(function(){
    xh.fire('akc-route-change','/user/4/profile/new');
  }).then(promiseTimeout(2000)).then(function(){
    xu.fire('akc-route-change','/user/6');
  }).then(promiseTimeout(10)).then(function(){
    expect(xu.params[0]).to.equal(6);
    history.back();
  }).then(promiseTimeout(10)).then(function(){
    expect(xu.params[0]).to.equal(4);
    done();
  });
});

回答by Jaromanda X

To make a timeout which works as you want, write a function which takes a delay, and returns a function suitable for passing to then.

要使超时按您的需要工作,请编写一个函数,该函数需要延迟,并返回一个适合传递给then.

function timeout(ms) {
  return () => new Promise(resolve => setTimeout(resolve, ms));
}

Use it like this:

像这样使用它:

Promise.resolve() . then(timeout(1000)) . then(() => console.log("got here"););

However, it is likely that you will want to access the resolved value of the promise leading into the timeout. In that case, arrange for the function created by timeout()to pass through the value:

但是,您可能希望访问导致超时的承诺的已解析值。在这种情况下,安排由创建的函数timeout()传递值:

function timeout(ms) {
  return value => new Promise(resolve => setTimeout(() => resolve(value), ms));
}

Use it like this:

像这样使用它:

Promise.resolve(42) . then(timeout(1000)) . then(value => console.log(value));

回答by ashish

await new Promise((resolve, reject)=>{
    // wait for 50 ms.
    setTimeout(function(){resolve()}, 50);
});
console.log("This will appear after waiting for 50 ms");

This can be used in an async function and the execution will wait till given interval.

这可以在异步函数中使用,并且执行将等到给定的时间间隔。

回答by philip oghenerobo balogun

This as already been answered above, but I feel this could be done easily with:

这已经在上面回答过,但我觉得这可以通过以下方式轻松完成:

const setTimeoutPromise = ms => new Promise(resolve => setTimeout(resolve, ms))

setTimeoutProisefunction accept wait time in msand passes it down to the setTimeoutfunction. Once the wait time is over, the resolve method passed down to the promise is executed.

setTimeoutProise函数接受等待时间ms并将其传递给setTimeout函数。一旦等待时间结束,就会执行传递给 promise 的 resolve 方法。

Which could be used like this:

可以这样使用:

setTimeoutPromise(3000).then(doSomething)

回答by Yom S.

Another approach for adding delays to Promises without having to predefine or importa helper function(that I personally like best) is to extend the property of the Promiseconstructor:

另一种无需预定义或辅助函数(我个人最喜欢)向Promises添加延迟的方法是扩展构造函数的属性:importPromise

Promise.prototype.delay = function (ms) {
  return new Promise(resolve => {
    window.setTimeout(this.then.bind(this, resolve), ms);
  });
}

I'm leaving out the rejectcallback since this is meant to always resolve.

我省略了reject回调,因为这意味着总是resolve.

DEMO

演示

Promise.prototype.delay = function(ms) {
  console.log(`[log] Fetching data in ${ms / 1000} second(s)...`);

  return new Promise(resolve => {
    window.setTimeout(this.then.bind(this, resolve), ms);
  });
}

document.getElementById('fetch').onclick = function() {
  const duration = 1000 * document.querySelector('#duration[type="number"]').value;

  // Promise.resolve() returns a Promise
  // and this simply simulates some long-running background processes 
  // so we can add some delays on it
  Promise
    .resolve('Some data from server.')
    .delay(duration)
    .then(console.log);
}
<div>
  <input id="duration" type="number" value="3" />
  <button id="fetch">Fetch data from server</button>
</div>

Or if you need it to also be .catch()-able, here is when you need the second argument (reject). Note that the catch()handling will also occur after the delay:

或者,如果您需要它也能.catch(),这是您需要第二个参数 ( reject) 的时候。请注意,catch()延迟后也会发生处理:

Promise.prototype.delay = function(ms) {
  console.log(`[log] Fetching data in ${ms / 1000} second(s)...`);

  return new Promise((resolve, reject) => {
    window.setTimeout(() => {
      this.then(resolve).catch(reject);
    }, ms);
  });
}

document.getElementById('fetch').onclick = function() {
  const duration = 1000 * document.querySelector('#duration[type="number"]').value;

  Promise
    .reject('Some data from server.')
    .delay(duration)
    .then(console.log)
    .catch(console.log); // Promise rejection or failures will always end up here
}
<div>
  <input id="duration" type="number" value="3" />
  <button id="fetch">Fetch data from server</button>
</div>