Needles to say I am doing something wrong. I need some help figuring out what it is.
Consider the following code (a simplified version of my problem):
function testTimer(time, msg,resolve){
console.log(arguments.callee.name);
window.setTimeout(
function() {
console.log("MSG:", msg);
if(resolve !== undefined)
resolve(1);
}, time * 1000);
}
new Promise(function(resolve, reject) {
testTimer(1, 'ONE', resolve);
}).then(function(resolved){testTimer(9, 'TWO');}, function(rejected){alert("Rejected!", reject)})
.then(function(resolved){testTimer(1, 'THREE'); }, function(rejected){alert("Rejected!", reject)});
The expected output is:
ONE
TWO
THREE
Instead since the first then requires 9 seconds to execute and the second then requires 1 second to execute I get:
ONE
THREE
TWO
The question is simple: How can I get the then's to wait for eachother ?
Thanks!
You need to return a promise that is resolved only after the time out fires:
function testTimer(time, msg,resolve) {
console.log(arguments.callee.name);
window.setTimeout(function() {
console.log("MSG:", msg);
if(resolve !== undefined) {
resolve(1);
}
}, time * 1000);
}
new Promise(function(resolve, reject) {
testTimer(1, 'ONE', resolve);
}).then(
function(resolved){
// here we return a promise - that is resolved by testTimer
// function. Notice that I'm passing a resolve function into it
return new Promise(function(resolve) {
testTimer(9, 'TWO', resolve);
});
},
function(rejected){
alert("Rejected!", reject)
}
)
.then(
function(resolved){testTimer(1, 'THREE'); },
function(rejected){alert("Rejected!", reject)}
);
How it works: testTimer function as you have defined it - accepts a callback as a 3rd argument that is to be invoked after the timer is fired. We employ that to resolve a nested promise that we set in the second step. So the third step is only happening after the second was resolved, which happens in the timer, so the order is kept as expected.
Related
I would like to execute functions one at a time and call another function when a function is finished. I was able to do it using callbacks but not using a promise chain.
Here is what I tried (based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#chained_promises) but it executes the first 3 functions at the same time instead of waiting 1 second in each function:
function displayAll() {
var myPromise = (new Promise(display1))
.then((new Promise(display2))
.then((new Promise(display3))
.then(display4)));
}
function display1(resolve) {
setTimeout(function () {
console.log("display1");
resolve();
}, 1000);
}
function display2(resolve) {
setTimeout(function () {
console.log("display2");
resolve();
}, 1000);
}
function display3(resolve) {
setTimeout(function () {
console.log("display3");
resolve();
}, 1000);
}
function display4(resolve) {
setTimeout(function () {
console.log("display4");
}, 1000);
}
Do you know what is wrong with the code and if it is possible to do what I am trying to do without callbacks?
In order to chain Promises (MDN) you need to return a promise in then method callback, instead you are constructing the promise as an argument of the then method.
This will trigger the Promise as soon as is "encountered" the new keyword, that is not the expected behaviour. You instead want to wait the first Promise to end, and then chain the then method that will create a new Promise:
function displayAll() {
var myPromise = (new Promise(display1))
// .then(new Promise(display2)) <-- you are calling here the promise
.then(function() {
return new Promise(display2) // <-- here we return a promise to chain
})
.then(()=> new Promise(display3)) // same with arrow functions
.then(display4);
}
From your code:
function displayAll() {
var myPromise = (new Promise(display1))
.then(()=> new Promise(display2))
.then(() => new Promise(display3))
.then(display4);
}
function display1(resolve) {
setTimeout(function () {
console.log("display1");
resolve();
}, 1000);
}
function display2(resolve) {
setTimeout(function () {
console.log("display2");
resolve();
}, 1000);
}
function display3(resolve) {
setTimeout(function () {
console.log("display3");
resolve();
}, 1000);
}
function display4(resolve) {
setTimeout(function () {
console.log("display4");
}, 1000);
}
displayAll()
Another more clear approach:
You can also make your display functions return a Promise such that you can pass them directly to the then method:
function display1() {
return new Promise(resolve => {
setTimeout(function () {
console.log("display1");
resolve();
}, 1000);
});
}
function display2() {
return new Promise(resolve => {
setTimeout(function () {
console.log("display2");
resolve();
}, 1000);
});
}
function display3() {
return new Promise(resolve => {
setTimeout(function () {
console.log("display3");
resolve();
}, 1000);
});
}
function display4() {
return new Promise(resolve => {
setTimeout(function () {
console.log("display4");
resolve();
}, 1000);
});
}
let myPromise =
display1()
.then(display2)
.then(display3)
.then(display4)
A walk through of displayAll's order of execution:
var myPromise = (new Promise(display1))
The Promise constructor calls display1 which sets up a timeout to log "display1" and resolve the promise. This works perfectly and the initial 1 second delay is honored.
.then(new Promise(display2))
calls the then method of myPromise during execution of displayAll.
The argument for then is evaluated before the making the call .
Creating then's promise argument causes the Promise constructor to call display2 which sets up a timeout relative to displayAll's time-of-execution.
When called then silently ignores the promise argument because it's not a function. Default handlers, used by then in the absence of callable arguments, pass through incoming data or promise rejection reasons.
.then(new Promise(display3))
operates the same as the previous then clause: set up a timer relative to displayAll's time-of-execution and use default handlers which pass through data or rejection reasons.
.then(display4)));
registers display4 as a handler to be called when the promise returned in step 3 becomes fullfilled. display4 sets up a workable timer to log "display4". Note display4's argument is now misnamed - the argument passed to successive fulfillment handlers is the value returned by the previous promise handler in the chain.
The expected output of calling displayAll is then to
Delay a second after displayAll was called and log "display1".
Delay a second, again after displayAll was called, and log "display2".
Delay a second, again after displayAll was called, and log "display3".
When promise chain handling executes display4 as a handler, set up a timer to log "display4" - so it logs one second after "display3".
One solution to stagger the execution of the display functions would be to move promise creation from where then arguments are calculated into the handler itself. The handler can return the promise to delay proceeding down the promise chain until the returned promise is resolved by the timer:
function displayN() {
return new Promise( resolve =>
setTimer( function() {
console.log("displayN");
resolve();
}, 1000)
);
}
Other solutions and approaches are possible. Promisifying setTimeout to create a promise that resolves after a specified interval can be useful for delaying steps in a promise chain or async function. As a fairly minimal example:
function delayPromise( msec) {
return new Promise(resolve=> setTimeout( resolve, msec));
}
As an aside only, the value of a promise chain is the promise returned by the last then, catch or finally call in the chain - promise chain values may be returned by a function but in practice at least are rarely recorded in a variable.
In the following code, can someone explain why calling secondMethod in the Promise chain works but calling secondMethod() does not?
function firstMethod() {
return new Promise(function(resolve, reject){
setTimeout(function() {
console.log('first method completed');
resolve();
}, 2000);
});
};
function secondMethod() {
return new Promise(function(resolve, reject){
setTimeout(function() {
console.log('second method completed');
resolve();
}, 2000);
});
};
function thirdMethod() {
return new Promise(function(resolve, reject){
setTimeout(function() {
console.log('third method completed');
resolve();
}, 3000);
});
};
// Works
firstMethod().then(secondMethod).then(thirdMethod);
// Doesn't work - executes secondMethod immediately after firstMethod
// firstMethod().then(secondMethod()).then(thirdMethod);
The second way doesn't work because you're calling the functions immediately, in a synchronous way, before any of the timeouts resolve.
Here's another way to think about what's happening:
// you're calling all your methods and creating promises before creating the chain
let first = firstMethod();
let second = secondMethod();
let third = thirdMethod();
first.then(second).then(third);
Because Promise.then takes one or two callbacks. It does not take a Promise.
When you immediately call secondMethod you're passing a Promise to .then.
It's essentially the same as doing this:
firstMethod()
.then(new Promise(...)) // Should be a function, not a Promise
I am not really sure I understand the difference between these two common scenarios.
Say we have this:
user.save().then(function(val){
anotherPromise1(val);
}).then(function(val){
anotherPromise2(val);
}).catch(function(err){
});
versus:
user.save().then(function(val){
return anotherPromise1(val);
}).then(function(val){
return anotherPromise2(val);
}).catch(function(err){
});
I know it makes a difference but how exactly?
If you don't return a value from the then callback, you're effectively returning undefined. The next then callback will run immediately, and see undefined as the resolution value.
If you return a promise from the then callback, the second then callback waits on that promise (indirectly, but that doesn't really matter), and when that promise is resolved, gets the resolution value from that promise.
(This is covered by the then specification in the Promises/A+ spec, but slightly by omission — it doesn't explicitly mention what should happen if onFulfilled doesn't return anything, but in JavaScript, calling a function always gives you a resulting value; if the function doesn't explicitly return something, undefined is the result of calling it. JavaScript doesn't have the concept of void methods a'la C/C#/C++/Java.)
You can see it in this script live copy on Babel's REPL:
let start = Date.now();
function elapsed() {
let rv = String(Date.now() - start);
while (rv.length < 4) {
rv = "0" + rv;
}
return rv;
}
function anotherPromise(type, val) {
console.log(`${elapsed()}: anotherPromise[${type}] got ${val}`);
return new Promise(resolve => {
setTimeout(() => { resolve(val * 2); }, 1000);
});
}
function anotherPromise2(type, val) {
console.log(`${elapsed()}: anotherPromise2[${type}] got ${val}`);
return new Promise(resolve => {
setTimeout(() => { resolve(val * 3); }, 10);
});
}
let user = {
save: () => {
return new Promise(resolve => {
setTimeout(() => {
resolve(42);
}, 10);
});
}
}
// Without return
user.save().then(function(val){
anotherPromise("without", val);
}).then(function(val){
anotherPromise2("without", val);
}).then(function() {
console.log(`${elapsed()}: All done`);
}).catch(function(err){
});
user.save().then(function(val){
return anotherPromise("with", val);
}).then(function(val){
return anotherPromise2("with", val);
}).then(function() {
console.log(`${elapsed()}: All done`);
}).catch(function(err){
});
The output is (for example):
0015: anotherPromise[without] got 42
0017: anotherPromise2[without] got undefined
0018: All done
0020: anotherPromise[with] got 42
1021: anotherPromise2[with] got 84
1032: All done
Note the differences between without a return and with a return:
Without, anotherPromise2 was called immediately (as we can see from the elapsed time values) and received undefined.
With, anotherPromise2 waited for anotherPromise's resolution to occur, and then received 84 (anotherPromise's resolution value)
The difference is the timing in this matter.
In example 1, the save promise is fullfilled and the anotherPromise1 will be invoked and because there is no promise returned, the anotherPromise2will be invoked immediately.
If you return the promise of the anotherPromise1 function, the invokation of anotherPromise will happen, after anotherPromise1 was resolved.
So example 1: anotherPromise1 and anotherPromise2 will be shot simultaneous
While example 2: anotherPromise2 will wait for anotherPromise1 to be resolved
I'm new to promises and trying to wrap my head around something that should be simple. Maybe someone can hit me in the head with it instead!
I've got these two functions:
//an arbitrary method that runs on delay to mimic an async process
method1 = function( _value, _callback ){
setTimeout(function(){
console.log('dependency1_resolved');
_callback.apply(this, [{valIs:_value}]);
}.bind(this), (Math.random() * 1000));
};
//something that can simple return the object
function returnVal(x){
console.log(x); //this logs 'Object {valIs: 4}'
return x;
}
due to it's async nature, I'd like to run this function in a promise to be used later (maybe even chain later) in my code.
here is my promise:
var promise = new Promise(
function(resolve, reject) {
var x = method1(4, returnVal);
resolve(x);
}
);
promise.then(function(val) {
console.log(val); // undefined
return val;
});
console.log(promise); //Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: undefined}
Does this have anything to do with the .apply method in the first function? What am I missing? Can someone please slap me?
Your returnVal callback doesn't actually do anything.
Instead, you need to actually pass a callback that accepts the value:
var promise = new Promise(
function(resolve, reject) {
method1(4, function(x) { resolve(x); });
}
);
You can also just pass resolve itself as the callback:
var promise = new Promise(
function(resolve, reject) {
method1(4, resolve);
}
);
Your method1 does return nothing, that's why x is undefined. That the returnVal callback does return something is insignificant, its return value is simply ignored by method1.
You will need to call resolve as the callback to make your code work:
new Promise(
function(resolve, reject) {
var x = method1(4, resolve);
}
).then(function(val) {
console.log(val); // undefined
return val;
});
See also How do I return the response from an asynchronous call?
It seems that if the 'resolve' function is not referenced in the function that is used to create a promise, then the promise is undefined. In the code below, ...
var count = 0;
var limit = 3;
//
var thePromise;
function timeout(_count) {
thePromise.resolve(_count);
}
function sendMessage() {
return new Promise(function(resolve, reject) {
if (++count > limit) {
reject({'Limit Exceeded': count});
}
else {
// With this line in place, the return value from this function
// (expected to be a promise) is undefined
setTimeout(timeout.bind(null, count), 1000);
// If the line above is replaced by this, it works as expected
// setTimeout(/*timeout.bind(null, count)*/function (_count) {
// resolve(_count);
// }.bind(null, count), 1000);
}
});
}
function sendAnother(_count) {
console.log('Resolved with count %j', _count);
return sendMessage();
}
function detectError(_error) {
console.log('Rejected with %s', JSON.stringify(_error));
process.exit(1);
}
thePromise = sendMessage();
thePromise = thePromise.then(function (_count) { return sendAnother(_count)}, function(_error) {detectError(_error)});
trying to do the resolve outside of the function that creates the promise, results in:
node-promises.js:6
thePromise.resolve(_count);
^
TypeError: undefined is not a function
at timeout (node-promises.js:6:16)
at Timer.listOnTimeout (timers.js:110:15)
But if line 16 is commented out and lines 18-20 are uncommented, the output is:
Resolved with count 1
.. which is what I expected. What am I missing? This is using nodejs v0.12.2 on Windows 7, if that makes any difference.
It happens because of this line:
thePromise.resolve(_count);
there is no resolve function on that object. resolve comes from the function you have created when instantiating the new promise:
return new Promise(function(resolve, reject) {
By commenting out that line, and using the alternate function, you are calling the correct resolve(), which causes the desired output. One option to fix this could be to pass the resolve function into your timeout call, eg:
function timeout(resolve, _count) {
resolve(_count);
}
Although I am not sure why you would want to do this.
Your title asks why new Promise is returning undefined, when the fact is that it isn't. It is indeed returning a valid promise. It is just that resolve is not a valid function on the promise object.