Why `async/await` doesn't work in my case? - javascript

I read about async/await, but I've a critical question.
At first I explain an old example to show base of my question and then I ask my exact question.
Everybody know it:
console.log('1');
console.log('2');
console.log('3'); // Ex: 123
It is simple but in below case:
console.log('1');
setTimeout(()=>{
console.log('2');
},0);
console.log('3'); // Ex: 132
It is simple too, setTimeout function is asynchronous and JavaScript jump from it and after resolve run its function, so we see 2 after 1 and 3.
But, now I read async/await and I wrote a function like this:
(async function test() {
console.log('1');
await setTimeout(()=>{
console.log('2');
},0);
console.log('3');
})(); // Ex: 132
The Export is 132 too, why? this is my question, why 3 run before 2? I expect because of async/await after 1 JavaScript wait for 2 and then wrote 3. why 132?

await only suspends when the value passed to it is a Promise. In your case, setTimeout returns a Number so await doesn't wait for it.
The correct code will be as follows:
async function test() {
console.log('1');
await new Promise((resolve, reject) => {
setTimeout(() => {
console.log('2');
resolve()
}, 0);
});
console.log('3');
}

Because setTimeout doesn't return a promise. await x only waits if x is a promise; if x isn't a promise, it's (effectively) wrapped in one as though you had await Promise.resolve(x). That means the code following it will run asynchronously but as soon as possible.*
If you want a promise version of setTimeout, see this question's answers. But even with that, your test function wouldn't use a callback, instead you'd just await the promise-enabled timeout:
function later(delay) {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
}
async function test() {
console.log("1");
await later(10);
console.log("2");
console.log("3");
}
test().catch(e => console.error(e));
console.log("After the call (just to prove it does wait for the timeout after 1 and before 2");
* On browsers, that's guaranteed to be before a setTimeout(..., 0) scheduled during the same task, because promise callbacks scheduled during a task occur just after the end of that task, before the next task is picked up from the queue (even if the next task was scheduled before the promise callback). More on this ("macrotasks" and "microtasks") in this question's answers.

You can await to that functions that returns Promise. setTimeout does not returns Promise. So in this case await used before the setTimeout does not has a sense.
You can wrap your setTimeout into a Promise and call resolve at the setTimeout function.
(async function test() {
console.log('1');
await new Promise((resolve, reject) => {
setTimeout(() => {
console.log('2');
resolve(); // also can pass a parameter here to get it via await.
},0);
});
console.log('3');
})();

Related

Why does this sequence of await function calls run in the wrong order?

I want to output some text after 2 seconds first, after output some "alert()" second and at the end output some "console.log" by using only async/await. Please help me how to write such a sequence?
Why the code below does not work
async function qaz()
{
let res1 = await setTimeout(function(){
console.log("show me first");
}, 2000);
let res2 = await alert('show me second');
let res3 = await console.log('show me at the end');
return [res1,res2,res3];
}
The setTimeout is part of the JavaScript asynchronous methods (methods that are starting to execute and their result will return sometime in the future to a component called the callback queue, later to be executed)
What you probably want to do is to wrap the setTimeout function within a Promise and await it and then execute the rest of the synchronous code.
const longTask = () => new Promise(resolve => {
setTimeout(function(){
console.log("show me first");
resolve();
}, 2000);
});
async function qaz()
{
await longTask();
alert('show me second');
console.log('show me at the end');
}
qaz();
I suggest to read more about the event loop model here
Neither the setTimeout, the alert, or the console.log return promises, and that's a problem because await only works with promises.
You can still use async/await, however. Create a delay function that returns a promise you can await for the first part, and then after that resolves do your alerts and your logging.
function delay(n) {
return new Promise(res => {
setTimeout(() => res(), n);
});
}
async function qaz() {
await delay(2000);
console.log('Show me first');
alert('Show me second');
console.log('Show me at the end');
}
qaz();

Not getting what's happening in the JavaScript script code below

async function async1(){
console.log(1)
await async2()
console.log(2)
}
async function async2(){
console.log(3)
}
console.log(4)
setTimeout(function(){
console.log(5)
}, 0)
async1()
new Promise(function(resolve){
console.log(6)
resolve()
}).then(function(){
console.log(7)
})
console.log(8)
4
1
3
6
8
7
2
5
Please explain how this code creating the above output. I'm confused with Why the 2 not come immediately after log(3). what is exactly happening after we use async await ?.
please provide some insights on this.
Since you are not doing await on async1 method invocation (async1()), The control will be on current execution context and console.log(2) will be moved to micro task queue in event loop.
Please execute your code again with changing await async2() in browser console and you will notice the expected order.
Update: Adding more details
The async functions return the promise.
When using await for async function execution means, the rest of code after the await will be executed after the async promise resolve.
To see the execution flow, I have converted to equivalent code which using directly promises.
console.log(4);
setTimeout(function () { // move to call back queue
console.log(5);
}, 0);
new Promise(function () {
console.log(1);
new Promise(function (resolve) {
console.log(3);
resolve();
}).then(function () { // move to micro task queue
console.log(2);
});
});
new Promise(function (resolve) {
console.log(6);
resolve();
}).then(function () { // move to micro task queue
console.log(7);
});
console.log(8);

How to execute Console.log and setTimeout sequentially in JavaScript

(function() {
console.log(1);
setTimeout(function(){console.log(2)}, 1000);
setTimeout(function(){console.log(3)}, 0);
console.log(4);
})();
Hello All,
When I run the above code, it produces the following output: 1,4,2,3
So, instead of producing 1,4,2,3 I want the output to be 1,2,3,4 but by using setTimeout() method only.
Can anyone please help me out on how to produce 1,2,3,4 by having setTimeout() method and making modifications to it.
To simplify things.. wrap the setTimeout with a Promise - that will give you the ability to also use async and await keywords and simplify your logic
(async function() {
function sleep(interval) {
return new Promise(resolve => {
setTimeout(resolve, interval)
})
}
console.log(1)
await sleep(1000)
console.log(2)
await sleep(1000)
console.log(3)
await sleep(1000)
console.log(4)
})();

Is this a correct understanding of async/await?

function injectText(value, selector) {
return new Promise((resolve, reject) => {
setTimeout(() => {
document.querySelector(selector).innerHTML = value
resolve()
}, 500)
})
}
async function printWord(word, selector) {
for (let i = 0; i < word.length; i++) {
await injectText(word.substring(0, i + 1), selector)
}
}
async function run() {
await printWord('Hello', '.hello')
await printWord('there!', '.there')
}
run()
<div class="hello"></div>
<div class="there"></div>
I've used Promise and async/await to print Hello there! letter after letter with the delay of 500ms. It works as intended, however, I'm not sure if I understand what happens when function run() executes.
await before printWord means that execution of async funtion run is paused until Promise is resolved or rejected.
Function printWord is an async function. I'm not returning anything from it, therefore undefined is returned at the end of function run. Async functions always return Promises, therefore, it automatically returns Promise which automatically resolves with undefined value? Is this what happens?
Then it jumps to second await printWord where the same logic applies.
Am I understanding it correctly? I appreciate your help.
Yes, the run() function's execution is paused whenever there is an await for it to handle. The function will pause its execution twice before resolving itself.
Yes, an async function does return a Promise; however, realize that promises resolve, and they don't return. At least, in javascript (since async/await is just sugar for Promises) they don't really return, but they resolve.
Yes.

"A little help!" with the asynchronous syntax in javascript. I'm an old newbie, and I've finding this more than a little frustrating

I’d just like to run an async function, have it wait till it has a return value, pass that return value to another async function and wash and repeat a few times. I’ve seen some examples but I’m struggling to make them work in test code. Forgive all the baseball context, but I had to have a little fun with this self-inflicted nose bleed.
I can't even make it work without passing return values in the attached code, feeling pretty stupid at the moment...
whosOnFirst(1)
.then(whatsOnSecond(2))
.then(iDunnosOnthird(3))
I'd like to see the syntax to have whosOnFirst(1) execute, pass a return value to whatsOnSecond() and pass it's return value to iDunnosOnthird(). Creating the desired output (less the spaces) referenced in the bottom of the snippet.
I really appreciate the help!
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
return pause + 1; // delayed return value / adds 1 to input param
}, pause * 1000);
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
waitSec("who's on first", 1).then(result => {
return result;
})
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
waitSec("what's on second", i).then(result => {
return result;
})
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
waitSec("iDunno's on third", i).then(result => {
return result;
})
}
whosOnFirst(1)
.then(whatsOnSecond(2))
.then(iDunnosOnthird(3))
// CURRENT OUTPUT:
// who's on first called, waiting 1 second(s)...
// what's on second called, waiting 2 second(s)...
// iDunno's on third called, waiting 3 second(s)...
// who's on first sent 2 in a return, and done!
// what's on second sent 3 in a return, and done!
// iDunno's on third sent 4 in a return, and done!
// DESIRED OUTPUT:
// who's on first called, waiting 1 second(s)...
// who's on first sent 2 in a return, and done!
// what's on second called, waiting 2 second(s)...
// what's on second sent 3 in a return, and done!
// iDunno's on third called, waiting 3 second(s)...
// iDunno's on third sent 4 in a return, and done!
Working Solution
Your issue is that the waitSec is in a different context from the rest of the code. You need to lift it to the Promise context.
Then you have "promisified" code all the way down, and you can chain promises. Here it is working (further explanation below it):
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise(resolve => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
function whosOnFirst(i) {
return waitSec("who's on first", 1)
}
// What's on Second - run after completion of whosOnFirst() using return for param
function whatsOnSecond(i) {
return waitSec("what's on second", i)
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
function iDunnosOnthird(i) {
return waitSec("iDunno's on third", i)
}
whosOnFirst(1)
.then(whatsOnSecond)
.then(iDunnosOnthird)
.then(console.log)
Further Explanation
Your original implementation of waitSec always returns undefined to the caller:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
return pause + 1; // delayed return value / adds 1 to input param
}, pause * 1000);
// return undefined happens here
}
The return inside the setTimeout does not return to your calling code, because your code never calls this function. It is invoked by the JS timer code, asynchronously.
Returning values from async code using a callback
The way to communicate a result out of there to a caller is to either take a callback as a parameter to the outer function and call that callback in the async function, like this:
function waitSec({callerName, pause, callback}) {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
callback(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
}
In which case you have the classic callback pattern of JS.
Using Promises, and why you might prefer them
Or, you return a Promise, and resolve it to return the value - as I demonstrated in the solution. For more about that, search for "how do I promisify a callback".
The benefit of doing it this way (with a Promise) is that Promises are composable, as demonstrated in the solution.
Promises are also await-able, so you can use async/await with Promise-returning functions.
You do not need to mark the Promise-returning functions as async.
If you mark a function as async, it returns a Promise, like this:
// A function marked async returns a Promise
async function simple() {
return 3
}
// Proof that it is a Promise
simple().then(console.log)
// The equivalent code explicitly returning a Promise - this is what `async` does
function simpleP(n = 0) {
return new Promise(resolve => resolve(n+1))
}
// Proof that it behaves the same
simpleP().then(console.log)
// Promise composition
simple()
.then(simpleP)
.then(console.log)
// Same functionality, using async/await
async function main() {
const num = await simple()
const res = await simpleP(num)
console.log(`${num} + 1 = ${res}`)
}
main()
Marking synchronous return functions async makes them composable with async code that actually needs a Promise to return a value. You can use it to lift your functions to the same async context and compose them.
// Synchronous function that is Promisified and composable
async function simple() {
return 3
}
To actually get a value out of an asynchronous function that takes a callback and provides its return via that, you must construct and return a Promise from your function, and then call the Promise resolve inside the callback that provides the value you want to communicate out.
function getDBRecordP(id) {
return new Promise((resolve, reject) =>
dbLib.get(id,
(err, result) => err ?
reject(err) :
resolve(result)))
}
In this case, you have to explicitly wire up the Promise machinery. This is pretty much the only time that you need to create and return a Promise.
What it is doing is wrapping a callback interface for you.
function getDBRecordP(id) {
return new Promise((resolve, reject) =>
dbLib.get(id,
(err, result) => err ?
reject(err) :
resolve(result)))
}
Notice in the callback example, you had to take a callback argument from the caller as a parameter, and then call that with the return value inside the callback that provides the value.
With the Promise machinery, you do take a callback argument, but it is not provided by the caller, it is provided by the Promise that you construct. And then you return the Promise to the caller.
The getDBRecordP function above is the essence of "how do I convert a function that takes a callback into a function that returns a Promise?".
The caller then passes in the callback to the the Promise.then() method.
So you have a machine that wraps the callback passing convention into a formal API that is composable, which callbacks are not.
The other aspect of the Promise machine is the .catch() method.
Whereas with callbacks, the convention has always been that a callback signature is (err, result), the Promise machine allows you to provide two callbacks - one for err and one for result.
Please utilize await keyword that is beautiful for async function. Your waitSec function should return a promise, also when you are resolving a promise, your value comes in .then((res)=> console.log(res)), use it for chaining like below:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise((resolve, reject) => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function() {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
const resp = await waitSec("who's on first", 1);
return resp;
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
const resp = await waitSec("what's on second", i);
return resp;
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
const resp = await waitSec("iDunno's on third", i);
return resp;
}
whosOnFirst(1).then((firstResp) => {
whatsOnSecond(firstResp).then((secResp) => {
iDunnosOnthird(secResp).then((thirdResp) => {
console.log(thirdResp);
})
})
})
You can also use the values in chain like below with async/await:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise((resolve, reject) => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function() {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
const resp = await waitSec("who's on first", 1);
return resp;
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
const resp = await waitSec("what's on second", i);
return resp;
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
const resp = await waitSec("iDunno's on third", i);
return resp;
}
let test = async() => {
var res1 = await whosOnFirst(1);
var res2 = await whatsOnSecond(res1);
var res3 = await iDunnosOnthird(res2);
console.log(res3);
}
test();
You need to await for each result like that:
async function main() {
const result1 = await whosOnFirst(1);
// whosOnSecond will not get called except after the whosOnFirst is done.
const result2 = await whosOnSecond(result1);
}
Here's how it's supposed to work:
function delay(milliseconds){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
}, milliseconds);
});
}
async function test(){
console.log('no delay, yet');
await delay(1000);
console.log('After 1 second');
await delay(1000);
console.log('2 seconds have passed');
await delay(3000);
console.log('Oh look, 5 seconds have passed!');
}
test();
async functions await until a Promise is resolved, then go to the next block of code after the Promise, possibly awaiting again... and again. I admit I thought it was strange that an async function really runs the awaits synchronously, but the async function itself it asynchronous... meaning another function executed immediately after an executed async function is likely to return results before the async function executes.
In regards to your example:
class Counter{
constructor(increment = 1, initially = 0){
this.increment = increment; this.count = initially;
}
inc(){
return this.count += this.increment;
}
dec(){
return this.count -= this.increment;
}
}
function delay(milliseconds){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
}, milliseconds);
});
}
const sec = new Counter;
function logCaller(callerName){
console.log(`${callerName} called, after ${sec.inc()} second(s)...`);
}
async function what(){
await delay(1000);
logCaller("who's on first");
await delay(1000);
logCaller("what's on second");
await delay(1000);
logCaller("iDunno's on third");
}
what();

Categories

Resources