Why does my code print in this sequence? (Javascript promises) - javascript

I'm trying to understand promises/asynchronous programming, and I don't understand why this code prints "1 2 0" rather than "2 1 0". From the third block of code: shouldn't f1 fire only after f2 logs "2" to the console?
const f1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(console.log(1));
}, 1000)
})
const f2 = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(console.log(2))
}, 1500)
})
f2.then(
f1.then(()=>{
setTimeout(()=>{
console.log(0);
}, 500)}
));
I'm trying to work on a program that needs several (like 6ish) asynchronous functions to fire in a very specific sequence, i.e. f2() must wait until f1() fully receives data from a request to execute, and I'm actually not sure how to do this...

You are running your f1 and f2 operations in parallel, starting them both one after the other. Both are started and then they are both in flight at the same time.
Then, you need to pass a function reference to .then() so it's a function that the promise infrastructure can call sometime in the future (when the host promise resolves). Instead, you are passing a promise to .then() which doesn't do anything useful.
If you change your code to this:
const f1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(console.log(1));
}, 1000)
})
const f2 = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(console.log(2))
}, 1500)
})
f2.then(() => {
f1.then(()=> {
setTimeout(()=>{
console.log(0);
}, 500)}
});
});
Then, you will start both the f1 and f2 timers immediately. Then, only after both f2 and f1 finish will you start the third timer and you should get the output 1 2 0. 1 comes first because it's a shorter timer than 2 which are both running in a parallel. 0 comes last because it's timer isn't started until both 1 and 2 are done.
If you want to chain them to get 2 1 0, then you have to not start 2 and 1 at the same time. Make f1 and f2 into functions, not promises so you can sequence when they are called.
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
function f1() {
return delay(1000).then(() => {
console.log(1);
})
}
function f2() {
return delay(1500).then(() => {
console.log(2);
})
}
function f3() {
return delay(1500).then(() => {
console.log(0);
})
}
// chain them together
f2().then(f1).then(f3).then(() => {
console.log("done");
});
This kind of .then() chaining basically says this:
Execute f2()
Register a .then() handler on the promise that .f2() returns so that when that promise resolves, it will then (and only then) call f1.
On the new promise that f2().then() returns, register a .then() handler on it so that when f1 is done, it will then call f3.
On the new promise that f2().then().then() returns, register a callback that will be called when f3 is done.

Related

Nested async/await functions

I am trying to understanding how to use async/await in nested manner.
In the example, I am calling function a() and inside that, I want to call b() and ensure that b() resolves before a().
The following is the code and although b() is called, it is not getting resolved.
function a() {
b();
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolved a');
}, 2000);
});
}
function b() {
console.log('calling b');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolved b');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
const result = await a();
console.log(result);
}
asyncCall();
The output of the code above is:
"calling"
"calling b"
"resolved a" (This line shows up after 2 seconds)
Then, I changed to code to include async/await in a(), (everything else is the same)
async function a() {
await b();
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolved a');
}, 2000);
});
}
function b() {
console.log('calling b');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolved b');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
const result = await a();
console.log(result);
}
asyncCall();
The output of the code above is:
"calling"
"calling b"
"resolved a" (This line shows up after 4 seconds [In the previous example it was 2 seconds])
But in both the case, 'resolved b' is not getting printed.
I have 2 questions:
How do I print 'resolved b' ?
Is it common to have cases like this where an async function (in this case, function "a"), is both the callee (it is called by asyncCall()) and the caller (it is calling b()) ?
How do I print 'resolved b' ?
Your code as it stands completely ignores the resolved value of b. The first example ignores the promise entirely. The second example waits for the promise to resolve but does nothing with the resolved value (not even capture it in a variable).
You have to console.log() it like you did everything else you logged.
You could logthisvalue = await b(). You could b().then(logthisvalue => here). You could modify b() so it logs its own value.
Is it common to have cases like this where an async function (in this case, function "a"), is both the callee (it is called by asyncCall()) and the caller (it is calling b()) ?
Yes.
The async and await keywords are tools to manage promises.
Top level await has poor support so, in general, you can only use await inside an async function.
You have to call an async function for it to do anything.
The async function has to get a promise to await from somewhere and usually that is by calling another function.

promise chain does not wait until other promise is resolved

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.

Why does this simple callback execute before the setTimeout?

This snippet logs 'output 2' ahead of 'output 1', which is cool due to the setTimeout.
const func1 = () => {
setTimeout(() => {
console.log('output 1');
}, 3000);
};
const func2 = () => {
console.log('output 2');
};
func1();
func2();
In this snippet, I used a callback, but it gave me the same result as the last snippet, why doesn't func1 execute before func2? How can I make func2 execute after func1?
const func1 = () => {
setTimeout(() => {
console.log('output 1');
}, 3000);
};
const func2 = () => {
console.log('output 2');
};
const main = (a, b) => {
a();
b();
};
main(func1, func2);
why doesn't func1 execute before func2
It does.
Func1 sets a timeout running
Func2 logs
3 seconds later the callback function you pass to setTimeout is called
If you want to make Func2 run after step 3, then you need to call it at the end of the callback you pass to setTimeout.
JavaScript does not wait for setTimeout to finish and then execute next functions.
whenvever javascript encounters a setTimeOut it store functions with provided timeout, in this case 3 seconds. after storing it in call stack it just starts to execute functions below it and since there is nothing in second function it get executed right away ( before 3 seconds obviously ). that's why you are seeing the log of second function before the log of first function.
To execute first function and then execute second function you can use promise chaining.
Example:
return new Promise(function(resolve, reject) {
setTimeout(() => {
console.log('output 1');
resolve();
}, 3000);
}).then(function(result) {
console.log('output 2');
});
Hope it will help. Feel free to correct if i am wrong.
Simple explanation -
Function 2 wouldn't wait for the completion of the timer started in function 1.
func1();
func2();
When you execute both functions, function 1 sets the timer and function 2 logs out.
Eventually, function 1 logs out after 3 seconds.
Your second snippet does pretty much the same.

Why can I not call resolve() for Promises in SetTimeout Function directly

I have two versions of a sleep function, one is waiting for the resolve the other isn't:
function sleephelper1(ms) {
return new Promise(function(resolve) {
setTimeout(() => resolve('done'), ms);
})
}
function sleephelper2(ms) {
return new Promise(function(resolve) {
setTimeout(resolve('done'), ms);
})
}
Then I call either sleephelper1 or sleephelper2:
async function test(){
var test = await sleephelper1(3000);
console.log(test)
console.log("exit test function")
}
test()
The first one is waiting 3 seconds before in resolves. But sleephelper2 ist not working properly. The code gets executed immediatly.
I thought that SetTimeout can delay the call of a function by a given amount of time. Is resolve() not a function ?
I have found this post JavaScript promise resolving with setTimeout which is quite what I am asking here, exept that I am using async await. Also I did not get the explanation. Could somebody explain to me why this behaves the way it does ?
setTimeout(() => resolve('done'), ms);
This means "create a function with the text () => resolve('done') and pass it into setTimeout". setTimeout will wait the specified amount of time, and then call that function.
setTimeout(resolve('done'), ms);
This means "immediately call resolve('done') and pass its result into settimeout". the return value from resolve is undefined, so undefined is passed into setTimeout. There is thus no function for setTimeout to run 3 seconds later.

Create a function that no matter how many times invoked run only when first async call finishes?

suppose I've a function fetch(id).
I would be calling it arbitrarily at random times.
But I want every successive call to run only after previous call has finished.
say the async task takes 4 seconds, and i call fetch 3 times. then total time should be 12 seconds.
I could make a array and at every call set promise to go through next in line.
but what are some ways to accomplish this.
I think I got it
//First my example function which could be anything but should return promise which would be queued
function example(n) {
return new Promise((res, rej) => {
setTimeout(()=> {
console.log(n);
res();
}, 1000);
});
}
//now solution
function debounce(func) {
let p = Promise.resolve();
return function(x){
p = p.then(() => func(x));
}
}
//usage
d = debounce(example);
d(1);d(2);d(3);d(4);d(5);d(6);d(1);
You can chain Promises without an array, just store a pointer to the last Promise
// async payload function
// returns a promise
function f(x) {
console.log(`call f(${x})`);
return new Promise((resolve) => {
setTimeout(() => {
console.log(`resolve f(${x})`);
resolve();
}, 2000);
});
}
// wrapper to call function `f` sequentially
// stores pointer to the last Promise in closure
const g = (function(){
let lastPromise = Promise.resolve();
return function(arg){
lastPromise = lastPromise.then(() => f(arg));
}
})();
// generate random calls of function function `g`
for (let i = 0; i < 5; i++) {
setTimeout(() => g(i), Math.random() * 100);
}
I guess you can use async.io library.
Or, each time you want to call your function, push the function itself in an array. When the previous function has finished, check if there is still some functions to call in the array.

Categories