Javascript Nested Promise not run in expected order - javascript

I tried to simplify my code, in essence I have this:
function thirdPartyAPIMethod() { // Dummy method returning promise
return Promise.resolve();
}
function func1() {
console.log("func1 start");
return thirdPartyAPIMethod().then(() => {
console.log("func1 end");
// ...
resolve();
});
}
function func2() {
console.log("func2 start");
// ...
console.log("func2 end");
}
func1().then(func2());
I want to run func1 and when it completes then run func2. So I was expecting the output would be this:
func1 start
func1 end
func2 start
func2 end
But instead it prints this:
func1 start
func2 start
func2 end
func1 end
Can someone help me to do this?

Modify your func1 to invoke resolve after thirdPartyAPIMethod's promise has been resolved
function thirdPartyAPIMethod() //dummy method returning promise
{
return Promise.resolve();
}
function func1() {
return new Promise((resolve, reject) => {
console.log("func1 start");
thirdPartyAPIMethod().then( () => {
console.log("func1 end");
resolve(); //invoke resolve here so that func1() is chained with func2
});
});
}
function func2() {
console.log("func2 start");
console.log("func2 end");
}
func1().then( () => func2()); // then has function callback handler instead of return value of func2

In the call to then of the first promise, you're not passing a reference to func2, but calling it:
func1().then(func2()).catch(...);
Change it to:
func1().then(func2).catch(...);
If you want to pass parameters:
func1().then(() => func2(...)).catch(...);
Or, using the pre-ES6 syntax:
funct1().then(function() { return func2(...); }).catch(...);
Besides, when you define the promise you're not calling resolve and reject, so theoretically (except if you haven't posted all your code) that promise never completes:
function func1(...) {
return new Promise((resolve, reject) => {
console.log("func1 start");
thirdPartyAPIMethod().then({
console.log("func1 end");
resolve(''); // resolve the promise with whichever value you want
})
});
}

func1(...).then(func2(...)).catch(...);
in above code func2() is executing before passing as input.
Correct way would be
func1(...).then(func2).catch(...);
P.s the following code is equivalent to what you want to achieve.
thirdPartyAPIMethod().then(func2).catch(...)

Related

How to react on function that setting timeout

I have function:
function a(){ setTimeout(()=>{console.log('a')}, 0) }
This function send console.log on very end of callstack isn't it?
How can I write a function that runs after this console.log
the only way i found is send function on the of of callstack in the same way
a();
setTimeout(()=>{ console.log('after a'), 0 });
But it looks bad for me. I tried with promise, but i can react then on 'a' function not on console.log inside.
function 'a' is uneditable
you can use "callback" , a reference to another function to the a( ) function. Example:
function yourFunctionToBeRunnedAfter(){
..
..
}
function a(callback){
setTimeout(()=>{
console.log('a');
calback(); // ==> here is your function execution.
}, 0);
}
a(yourFunctionToBeRunnedAfter);
if you have parameters to pass to the callback() call, you can use apply() or call() or bind() or the spread operator (...) example:
function a(callback,...params){ // ... is the spread operator
setTimeout(()=>{
console.log('a');
callback([...params]); // ==> here is your function execution by spreading your params to the callback call.
}, 0);
}
a(yourFunctionToBeRunnedAfter, 1,2,3);
You need to use a promise. I'd recommend using async-await, but it really depends on your preference.
function a () {
return new Promise(resolve => {
setTimeout(() => {
console.log('a')
resolve()
}, 0)
})
}
a().then(() => console.log('after a'))
If you need to know when timeouts finish often, I'd recommend making a helper function:
const wait = (time) => new Promise(r => setTimeout(r, time))
Then you can use it like so:
(async () => {
console.log('Hello')
await wait(1000) // wait 1 second
console.log('Hello a second later')
})()

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.

Call a function inside resolve

I'm studying promises; can anybody explain me why this piece of code does not work is I call the
function add() inside the resolve?
<script>
async function f() {
function add() {
return 14+3;
}
let promise = new Promise((resolve, reject) => {
setTimeout(()=>{resolve(add)}, 3000); //this doesn't work
setTimeout(()=>{resolve(14+3)}, 3000); // this works
});
let result = await promise;
alert(result);
alert ("END");
}
f();
</script>
When you resolve with value you actually returning value from promise. In your example you resolve with function in arguments, so your promise returns function(function add)
So modify line,
resolve(add);
to
resolve(add());

When is async function promise resolved?

I have below code:
let func = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log("two");
resolve();
}, 3000);
})
};
let func2 = async () => {
console.log("one");
await func();
console.log("three");
}
(async () => {
await func2();
console.log("main"); // This should never be executed
})()
Noticed func2 never returns a value, the promise returned by func2 should never be fullfilled in my opinion. Hence console.log("main") should never be executed. However it is executed after console.log("three"). Can somebody explain this to me?
Noticed func2 never returns a value, the promise returned by func2 should never be fullfilled in my opinion.
That is not how async functions work. Your func2 returns when it is done executing. The return value of a function with no return statement is the specific value undefined. So, undefined becomes the resolved value of the promise. Remember, in Javascript, undefined is a specific value. It's as if you did return undefined at the end of your function block. So, since undefined is the return value, that becomes the resolved value of the async promise.
To fully cover all the bases, async functions always return a promise and that promise gets a resolved/rejected value one of these ways:
1. When you explicitly return a value from the function. That becomes the resolved value of the promise that the async function returns.
async function test() {
return "hello";
}
test().then(val => {
console.log(val); // "hello"
});
2. When you throw an exception. The exception becomes the reject reason of the promise that the async function returns.
async function test() {
throw new Error("ouch");
}
test().catch(err => {
console.log(err); // Shows error object with message "ouch"
});
3. When you return another promise. That promise is chained to the one that the async function returns and the promise that the async function returns will follow the one you returned (resolve when it resolves with the same value or reject with the same reason).
async function test() {
return new Promise(resolve => {
setTimeout(() => {
resolve("hi");
}, 1000);
});
}
test().then(val => {
console.log(val); // "hi"
});
4. When you return nothing. That is the same as in regular functions and it's equivalent to a return undefined at the end of the function block so the resolved value takes on the value of undefined.
So, this:
async function test() {
console.log("hi");
}
test().then(val => {
console.log(val); // undefined
});
Works exactly the same as this:
async function test() {
console.log("hi");
return undefined;
}
test().then(val => {
console.log(val); // undefined
});
A function that does not explexitly return something, actually returns undefined:
function test() { }
console.log(test());
Its the same with async functions, they also resolve to undefined when no other value was returned.
An async function doesn't need a return value in order to resolve. It is considered resolved when it finishes executing without an error.
If you throw new Error() inside of func2, console.log("main") will never be executed.
When you don't return from a function it implicitly returns a default value — normally undefined. So the promise returned by func2 will still resolve when the function returns.
From MDN:
A function without a return statement will return a default value. In
the case of a constructor called with the new keyword, the default
value is the value of its this parameter. For all other functions, the
default return value is undefined.
You can see this in your code if you alter it to this:
(async () => {
func2()
.then(d => console.log(d));
})()

Return promise variable of main function after the "then" branch

I have the below code snippet. It has a function fn1 that defines a promise variable. The promise stores the promise object return by fn2. I invoked then on that promise object and finally return the promise.
function fn1() {
var promise = fn2(a, b);
promise.then(function() {
console.log('handling then');
console.log('doing something');
});
return promise;
}
Since the then branch is async code, my understanding is when the inner function is async the outer function also behaves in async way. But the problem is the promise object is returned before the code inside then completes.
I tried to return the promise object within then as below. But this doesn't return the promise defined inside fn1.
function fn1() {
var promise = fn2(a, b);
promise.then(function() {
console.log('handling then');
console.log('doing something');
return promise;
});
}
Please help, how do I workaround this. Thank you.
You want to do this:
function fn1() {
var promise = fn2(a, b);
return promise
}
fn1().then(function () {
console.log('handling then');
console.log('doing something');
});
Or you could just do this:
fn2(a, b).then(() => { /* do something */ })
But the problem is the promise object is returned before the code inside then completes.
Then your concept of async is flawed. Async does not block execution. A promise is an object that represents a pending operation. It's what you hold to know if that operation completed or not. You attach callbacks to promises to know if it did.
What you appear to be doing is to know if fn2 completed via fn1, doing processing along the way.
function fn1() {
return fn2(a, b).then(function() {
console.log('handling then');
console.log('doing something');
});
}
fn1.then(function(){
// fn2 completed, fn1 completed handling
});

Categories