How do you wrap setTimeout in a promise [duplicate]

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

.then(promiseTimeout(2000)).then

will not do what you expect. The “signature” for .then is 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.

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

You are not passing a function to 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:

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

The first outputs ‘hello’ immediately

The second outputs 2000 after 2 seconds

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

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

Firstly, I would define promiseTimeout differently

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

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

and the original code in the question’s it block can now be used unchanged

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();
  });
});

Leave a Comment