Monday, May 20, 2024
 Popular · Latest · Hot · Upcoming
197
rated 0 times [  200] [ 3]  / answers: 1 / hits: 47784  / 9 Years ago, sat, november 21, 2015, 12:00:00

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.



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.



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.fire call and a second one on the xu.fire call and would have expected a two second gap when a continues from the first breakpoint to the second.



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



What am I doing wrong?


More From » promise

 Answers
19

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

[#64318] Thursday, November 19, 2015, 9 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
dylondarrianb

Total Points: 48
Total Questions: 109
Total Answers: 104

Location: Zambia
Member since Thu, Jun 25, 2020
4 Years ago
;