Async vs then in Promise resolve - javascript

In the following code, I'm using a Promise with .then(), but it doesn't wait for setTimeout() as I'd expect.
Why does async/await wait for setTimeout() though? It looks like await waits for the Promise to resolve, but .then() doesn't. Can someone please provide some details?
function hello() {
console.log('hello');
}
function myPromiseFunction() {
return new Promise((resolve, reject) => {
setTimeout(function() {
console.log('hey');
resolve();
}, 2000);
});
}
myPromiseFunction().then(hello()); // Prints 'hello' then 'hey'
async function myAsyncFunction() {
await myPromiseFunction();
hello();
}
myAsyncFunction(); // Prints 'hey' then 'hello'

myPromiseFunction().then(hello()) uses the result of calling hello() as the callback to .then(). If you wish to use hello() as the callback function itself, use either of these syntaxes:
myPromiseFunction().then(() => hello());
myPromiseFunction().then(hello);
Note that the second syntax silently passes a parameter to hello() - the value resolved by the Promise. In your example this isn’t a problem (since hello() doesn’t check use its arguments at all), but it may be the cause of gotchas down the line.

Try
myPromiseFunction().then(()=>hello())
You are calling a function, not providing a function to call.

You are invoking hello with ()
.then(hello());
hello is called and returns void, you are then passing void into the then as the callback function.

Related

A weird thing with async functions in JavaScript

$ cat x.js
async function print() {
console.log("abc");
}
print();
$ nodejs x.js
abc
How can it be?! print() returns a Promise object that isn't awaited, is it? If it is not awaited, then why is the console.log executed?
yeah, like choz said ,async functions returns a promise, even if you haven't defined a promise in your code.
i think this returned promise turns fulfilled after all promises in await statement get resolved.
i test it in the following code, it also returns promise, which only turns fulfilled after all promises go resolved(after 3000ms in this case):
async function print2() {
await console.log("abc")
await new Promise((res, rej) => {
setTimeout(() => {res(33)},3000)
})
await new Promise((res, rej) => {
setTimeout(() => {res(33)},50)
})
}
You could say that an empty function itself, returns undefined (Which actually doesn't return anything). Take a look at the sample below;
function print() {}
var returnVal = print(); // undefined
You'll notice that returnVal is undefined.
Now, if we have something in the body of test() there, but you still don't pass any return value, then returnVal will still be undefined.
function print() {
console.log('abc');
}
var returnVal = print(); // undefined
So, in order for a function to have a return value, one simply just needs to return it.
function print() {
console.log('abc');
return 1;
}
var returnVal = print(); // 1
When you conver it to async function. Quoting from MDN
The body of an async function can be thought of as being split by zero
or more await expressions. Top-level code, up to and including the
first await expression (if there is one), is run synchronously. In
this way, an async function without an await expression will run
synchronously. If there is an await expression inside the function
body, however, the async function will always complete asynchronously.
Code after each await expression can be thought of as existing in a
.then callback. In this way a promise chain is progressively
constructed with each reentrant step through the function. The return
value forms the final link in the chain.
Now, based on the information above, here's what we know that refer to your question;
Your print() does not return anything, which should be undefined
The async function will complete asynchronously, meaning it will always return a Promise. Either pending, fulfilled or rejected
So, to say it in your question, here's what your function actually does;
async function print() {
console.log("abc"); // Prints 'abc'
}
// Function above is equivalent to;
function printEquivalentInNonAsync() {
console.log("abc"); // Prints 'abc'
}
var returnVal = print(); // `returnVal` is `undefined`
And, to answer your question
If it is not awaited, then why is the console.log executed?
Regardless the async function is awaited, it still executes! - Awaits just to ensure the line execution is halted until the Asynchronous function (Promise) has reached fulfilled or rejected. Note that, the first state of a Promise is pending.

Async function passed as an argument in place of Promise, showing different results

I'm working with PWA, the cache.open method takes a promise as argument. while the normal flow of code looks like this:
event.waitUntil(
caches.open().then().catch();
);
// output: as expected
I changed the argument to an explicit promise, and a async func. The promise is working the same way as expected but async function isn't.
For Promise
event.waitUntil(
new Promise(resolve => {
console.log('test');
resolve();
})
);
// output: as expected
For Async func
event.waitUntill(async () => await console.log('test'));
// output: no output
I want to know why async function isn't working as expected even though async itself returns a promise.
As Mark mentioned in the comment, async functions return promises when called. They aren't promises themselves. To make it work, you need to call that async function. You can think of async functions as promise generators. This should make it work:
const asyncFn = async () => await console.log('test')
event.waitUntill(asyncFn());

Understanding javascript promises and async/await to run synchronous code [duplicate]

This question already has answers here:
Why is the method executed immediately when I use setTimeout?
(8 answers)
How to turn this callback into a promise using async/await?
(2 answers)
Closed 3 years ago.
I already posted a question on this subject here on StackOverflow. Following the recommendations, I have tried to simplify my question.
I don't understand how to make synchronous code with Promises and their new polished version using async/await, despite the fact I have read and tried different approaches.
Here, I link some good documentation on the subject, in case it can help people in the future. Basically, Promises are an answer to callback hell and a way to make code execute after some other code has been executed, making things happening synchronously since JavaScript (hence Node.js) is asynchronous by nature. Async/await is built on top of Promises and simplify the code.
understanding JavaScript Promises
modern JavaScript with async/await
Async/await explained with examples
why moving from Promises to async/await
Despite I seem to understand the concept, I can't explain the results I obtain. To better my comprehension, here is a test file using node.js -v v8.10.0
/* This file try to use Promises and async/await to run code synchronously */
/* declaration of some Promisified functions */
const doSomethingAsync = () => { // direct declaration using new Promise and Promise.resolve()
return new Promise(resolve => { // This is just a promise, necesiry to wait for it
setTimeout(() => resolve('I want to be first'), 3000);
});
};
const test1 = async () => { // indirect Promise declaration usind async
return setTimeout(() => console.log("test1, I want to be executed first"), 3000); // async => the function returns a promise hence return a resolve,
}; // we should use await to wait for the Promise.resolve()
/* create intermediate calling functions making code synchronous to use console.log() properly */
const doSomething = async () => {
console.log( await doSomethingAsync());
};
const callTest = async () => {
console.log("hope to be executed before test1() resolve");
await test1();
console.log("hope to be executed after test1() resolve");
}
/* hope to run code synchronously in the following using .then() keyword */
console.log('1');
doSomething();
console.log('2');
callTest();
console.log('3');
doSomething()
.then(console.log("I want to be after"))
.catch(error =>{
console.log(error);
});
console.log('4');
console.log( doSomethingAsync().then(console.log("I want to be after too")) );
console.log('5');
// hope to run code synchronously in the following using await keyword
(async () => {
let test2 = await doSomethingAsync();
let test3 = await doSomething();
await test1();
console.log(`value doSomethingAsync: ${test2}`);
console.log(`value doSomething: ${test3}`);
})();
console.log('6');
What I expect is to see the code putted into the .then() keyword to be executed synchronously after Promise.resolve() fire... but it's not what I observe. See below the output of the program in my terminal:
ʘ node test_promises.js
1
2
hope to be executed before test1() resolve
3
I want to be after
4
I want to be after too
Promise { <pending> }
5
6
hope to be executed after test1() resolve
test1, I want to be executed first
I want to be first
I want to be first
I want to be first
value doSomethingAsync: I want to be first
value doSomething: undefined
test1, I want to be executed first
From the output, it is clear that .then() runs before the code inside the called function.
from StackOverflow
The success or the error handler in the then function will be called only once, after the asynchronous task finishes.
It seems not to be the case. Hence my question is straightforward:
Question: Why is .then() called before the asynchronous tack finish, and how to properly code with Promises, .then() and async/await (which is supposed to replace then)?
EDIT
From the answer of the community, I understand that .then() fires directly when the values passed to it are not callback functions. Hence, by modifying the .then() like so .then(function() {console.log("I want to be after")} )
Ok, I had not understood. I get
ʘ node test_promises.js
1
2
hope to be executed before test1() resolve
3
4
Promise { <pending> }
5
6
hope to be executed after test1() resolve
test1, I want to be executed first
I want to be after too
I want to be first
I want to be first
I want to be after
I want to be first
value doSomethingAsync: I want to be first
value doSomething: undefined
test1, I want to be executed first
I still do not understand why the console.log()s fire before the await function call in
const callTest = async () => {
console.log("hope to be executed before test1() resolve");
await test1();
console.log("hope to be executed after test1() resolve");
}
Thanks for the patience of the community
EDIT 2
From the answers of the community:
An async function only waits for the things that are awaited inside
its code. It does not automagically recognise any calls that do
something asynchronous. You need to explicitly promisify setTimeout
to make it work with promises. – Bergi ANSWER 2: return setTimeout(()
=> console.log("test1, I want to be executed first"), 3000) isn’t correct, because setTimeout doesn’t return a promise. Converting
functions that use callbacks, like setTimeout, without using other
helper functions is a little wordy: return new Promise(resolve => {
setTimeout(resolve, 3000); }); – Ry-♦
This means the correct code is the following:
// declaration of some Promisified functions
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I want to be first'), 3000);
});
};
const test1 = async () => { // IMPORTANT CORRECTION: use Promise constructor
return new Promise(resolve => { setTimeout( () => {resolve("test1, I want to be executed first")}, 3000); }); // actuallu async do not transform the return
}; // function into a Promise
// create intermediate calling functions making code synchronous to use console.log() properly
const doSomething = async () => {
console.log( await doSomethingAsync());
};
const callTest = async () => {
console.log("hope to be executed before test1() resolve");
const resultTest1 = await test1(); // IMPORTANT CORRECTION: declare a variable to return the value of the executed function when exec is over, using await
console.log(resultTest1);
console.log("hope to be executed after test1() resolve");
}
// hope to run code synchronously in the following using .then() keyword
console.log('1');
doSomething();
console.log('2');
callTest();
console.log('3');
doSomething()
.then(function() {console.log("I want to be after")} ) // IMPORTANT CORRECTION: declare a callback function instead of executing the function
.catch(error =>{
console.log(error);
});
console.log('4');
console.log( doSomethingAsync().then(function() {console.log("I want to be after too")}) );
console.log('5');
// hope to run code synchronously in the following using await keyword, THIS IS THE "RIGHT" WAY TO USE ASYNC/AWAIT
(async () => {
let test2 = await doSomethingAsync();
let test3 = await doSomething();
await test1(); // this is the last await, it will be the last executed code
console.log(`value doSomethingAsync: ${test2}`);
console.log(`value doSomething: ${test3}`);
})();
console.log('6');
A special thank to everyone that helped. I hope this will be useful.

Mocha throws "Resolution method is overspecified"

I'm trying to write a simple test -
describe("My test", function(){
it("Succeed", function*(done){
yield testFunc();
done()
})
})
function *testFunc(){
console.log("Hey!")
}
Note that I'm using co-mocha so that my generator will be executed.
The error that I get is -
Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.
Now, the docs clearly state -
when a test injects a callback function (suggesting asynchronous execution), calls it, and returns a Promise, Mocha will now throw an exception
https://github.com/mochajs/mocha/blob/master/CHANGELOG.md#boom-breaking-changes
However, I'm not returning a Promise! I'm injecting the done function which is a callback, but I'm not returning a Promise... yielding testFunc doesn't return a Promise.
So, why is this test throwing an error?
Thanks
The generator (function*) or rather it's coroutine-creating wrapper that's implicit here is most likely returning a promise. I'm not exactly sure how the co-mocha works here but maybe this would work:
describe("My test", function () {
it("Succeed", function* () {
yield testFunc();
return;
});
});
The return is not needed here, just added for clarity.
But you may need to change:
function *testFunc(){
console.log("Hey!")
}
to something like:
let testFunc = co.wrap(function* () {
console.log("Hey!");
});
for your yield in the code above to work.
If you're using co coroutines then your generators should yield promises. Here, you're generator yields the result of running a generator function that itself returns a generator, not a promise.
Your assumption that no promise is used is incorrect. co-mocha uses co.wrap internally to wrap the functions you pass to it. The documentation for co.wrap says:
Convert a generator into a regular function that returns a Promise.
Emphasis added.
Removing done as a param worked for me! Example is as follows:
BEFORE:
it('receives successful response', async (done) => {
const getSomeData = await getResponse(unitData, function callBack(unit, error, data){
expect(data.statusCode).to.be.equal(200);
done();
}) })
AFTER (works):
it('receives successful response', async () => {
const getSomeData = await getResponse(unitData, function callBack(unit, error, data){
expect(data.statusCode).to.be.equal(200);
}) })

How to await a previously-started function in ES7?

I have two asynchronous methods that return values, one of which is needed immediately and the other might be in use, based on the first result. However, I don't want to wait for the first result to come in before kicking off the second method and create an execution sequence dependency.
Using ES7 syntax, I would assume that await-ing a Promise would be the same as await-ing a function that returns a Promise, but it doesn't work:
async function tester() {
async function foo() { await setTimeout(() => {}, 2000)}
async function bar() { await setTimeout(() => {}, 1000)}
let rcFoo = foo();
let rcBar = await bar();
if (true) { // Some conditional based on rcBar
await rcFoo;
console.log(rcFoo); // It's a Promise object
}
}
Two questions:
Am I even understanding the asynchronous nature of Javascript correctly? If the above code would have worked, would it have achieved my goal of running two asynchronous functions concurrently-ish, or is it just not possible to do it?
Is it possible to somehow await an object that is a Promise like I tried, or does it have to reference a function directly?
Thanks..
In your code, foo will start right away, and then bar will start. Even if foo finished first, your code still awaits for the bar promise to finish before proceeding.
You can await everything as long as it's a promise, whether it's a variable or a returned value from a function call. As far as I know (I might be wrong), setTimeout doesn't return a promise, but it would be easy to convert it to one:
async function foo() {
return new Promise(resolve => setTimeout(resolve, 2000))
}
async function bar() {
return new Promise(resolve => setTimeout(resolve, 1000))
}
I would assume that await-ing a Promise would be the same as await-ing a function that returns a Promise
The same as awaiting the result of a call to a function that returns a promise, yes (which is awaiting a promise).
but it doesn't work:
await rcFoo;
console.log(rcFoo); // It's a Promise object
It does work, only awaiting a variable doesn't change that variable. You can only await values, and the result of the promise will become the result value of the await expression:
let res = await promise;
console.log(res); // a value
console.log(promise) // a promise for the value
That said, setTimeout doesn't return a promise, so your code doesn't sleep three seconds. You will have to promisify it, like e.g. #PedroMSilva showed it.

Categories