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

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.

Related

How are the callbacks of promises put in the micro task queue? [duplicate]

This question already has answers here:
What is the intention behind clause 2.2.4 of Promise/A+ spec?
(1 answer)
Why are javascript promises asynchronous when calling only synchronous functions?
(2 answers)
Why are Q.js promises asynchronous after they have been resolved?
(2 answers)
Closed last year.
As I understand a promise is something that can resolve() or reject(), and this action should be done after some work as completed, so it should be invoked as part of the callback like in this example:
let timeout = new Promise((resolve) => {
console.log('function called');
setTimeout(() => {
console.log("timeout finisced!");
resolve()
}, 1000)
});
timeout.then(() => {
console.log("timeout resolved!")
});
console.log('teminated!');
result
function called
teminated!
timeout finisced!
timeout resolved!
But the thing I can't understand is how they work when there isn't an asynchronous action to do and so, if you think synchronously, the resolve function should be called in the constructor of the promise, even if you don't have a handler for it and then the then() method should be able to handle it after; but from what I can see from this example, the code is executed in the same way.
So can someone explain the thought behind why the following example works in this way?
new Promise(function(resolve) {
console.log('function called')
resolve();
}).then(() => {
console.log('function resolved');
});
console.log('teminated!');
result:
function called
teminated!
function resolved
Edit:
I have mostly understanded what’s happening, the thing that now I am not sure is how this is done.
So correct me if I’m wrong, the .then() method is always put in the micro task queue no matter what, so even if you pass it synchronous code, it is not executed in the stack directly like other synchronous code.
And for that to happen the state of the promise should be fulfilled or rejected, and to do that the function resolve() or reject() should be called.
So now I would like to understand the process that is behind it, like what does the then() method effectively do?
You pass it a callback and that is stored in the promise object or what?
And because the callback is put on the micro task queue after it is fulfilled or rejected, does that mean it is the resolve() function that puts it in the micro task queue?
That should be a reasonable assumption based on this example below; or does the resolve function only change the state and then is the then() method that puts it in the queue?
const p = Promise.resolve();
p.then(() => console.log("callback 1"));
const p2 = Promise.resolve();
p2.then(() => console.log("other micro task"));
p.then(() => console.log("callback 2"));
/*
result:
callback 1
other micro task
callback 2
*/
const p = Promise.resolve();
p.then(() => console.log("callback 1"));
const p2 = new Promise((resolve) => {
setTimeout(() => resolve(), 0)
});
p2.then(() => console.log("other micro task"));
p.then(() => console.log("callback 2"));
/*
result:
callback 1
callback 2
other micro task
*/
The only thing I’m sure of is that there are multiple callbacks for a single promise, they aren’t always executed one by one in order, but there can be another micro task before it like in the first example.
I'm aware that this may be a stupid problem, but for some reason it put's me in confusion, so thank's in advance for the help.
First, promises are async (then part), async/await is built on top of them. And the promise is not a simple async code, but a micro task, its executed after sync code, but before macro tasks (such as timeouts).
So in your case we have sync promise part function called, than sync code teminated, than async part after promise resolved function resolved.
To better undestand how micro/micro tasks and event loop works, I recommend you to check out answers here: Difference between microtask and macrotask within an event loop context

What's the difference between setTimeout and Async await?

I sort of understand what each is doing. setTimeout, "The setTimeout() method calls a function or evaluates an expression after a specified number of milliseconds." Async await, returns a promise, and is just a function that can be put in a queue and have the results of the function checked in later.
But both allow me to "delay code", they are both asynchronous functions. So when would you use one rather than the other one?
Thank you for any help!
They are completely different.
Using async/await allows you to consume Promises in a flat manner in your code, without nesting callbacks or hard-to-read .then chains. For example:
const doSomething = async () => {
await asyncStep1();
await asyncStep2();
await asyncStep3();
};
where each async step returns a Promise.
await only allows you to "delay" code in a block if you already have a Promise to work with (or something that you convert to a Promise).
setTimeout is not similar at all to async/await - setTimeout doesn't consume a Promise or have anything to do with Promises at all. setTimeout allows you to queue a callback to be called later, after the timeout time has finished.
Unlike await, setTimeout does not delay code in a block - rather, you pass it a callback, and the callback gets called later.
Cool question.
You're right that they're both asynchronous code.
As a simple answer to the difference. If you are doing something like:
const fetchUser = async () => {
const result = await fetch("/user");
return await result.json();
}
async main1() {
setTimeout(() => console.log("done", 2000));
}
async main2() {
await fetchUser();
console.log("done");
}
The setTimeout is always going to take ~2000ms before it logs "done", provided you haven't blocked the thread somewhere else. The fetchUser is going to log "done" when the API is complete, that might be 100ms, that might be 10000ms.
But the difference is a bit more involved than that:
It's partly to do with the difference in a callback style of code or a promise style of code.
The callback style is an older of coding, the difference here is a bit more than that.
Let's set aside setTimeout for a moment and talk about two ways you might make a fetch call.
const fetchUserOne = async() => {
const result = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const json = await result.json();
return json;
}
const fetchUserTwo = () => {
return fetch('https://jsonplaceholder.typicode.com/todos/2')
.then(response => response.json())
};
async function main() {
const result1 = fetchUserOne();
const result2 = fetchUserTwo();
console.log(result1);
console.log(result2)
const awaitedResult1 = await result1;
const awaitedResult2 = await result2;
console.log(awaitedResult1);
console.log(awaitedResult2);
}
main().then(console.log("complete"));
Now what I want to highlight here, is that although the second example uses a callback style - they are both using promises.
When you run the code (and press F12 to see the full object), you'll note that the log of result1 and result2 are promises.
Window.setTimeout on the other hand does not use a Promise.
const result = setTimeout(() => {
console.log("done");
}, 2000);
console.log(result);
The return value of the setTimeout is a number, which can be used to cancel the timeout.
And that brings us to the main difference:
Promises are not cancellable, setTimeout is
See this Stack Overflow question about cancelling a promise.
To show an example of this, let's modify our example above.
const fetchUser = async () => {
const result = await fetch("https://jsonplaceholder.typicode.com/todos/1");
return await result.json();
}
let timeout;
async function main1() {
console.log("start main1");
timeout = setTimeout(() => console.log("done timeout"), 2000);
}
async function main2() {
console.log("start main2");
await fetchUser();
console.log("done fetchuser");
}
main1();
main2();
clearTimeout(timeout);
Above, we can see that we can quite easily abort the timeout call, whereas we can't cancel a promise directly.
This is not to say that you can't cancel that fetch request, as you can see in this thread, but you can't cancel the promise itself.
Firstly, setTimeout is static timer that depends on the given time. On the other hand async await is something that is not static. It will wait until it awaited function or promise return any response or error
Secondly, You can Timeout any execution but you cannot await any function.
Here is the Official doc Async await
setTimeout allows you to delay execution for an approximate amount of time.
let log = () => console.log('log');
// invokes `log()` after a delay
setTimeout(log, 1000);
async/await helps you deal with promises without function handlers; it's just syntactic sugar. It doesn't actually delay execution.
let promise = Promise.resolve(5);
let main = async() => {
// without await
promise.then(value => console.log(value));
// with await
console.log(await promise);
};
main();
Both async await and setTimeout aren't similar eventhough it looks like they are but they aren't.
If you are new to JavaScript then, you can think of setTimeout as a timer, so whatever block of code or function is passed to setTimeout it will execute after a fixed time, it basically delays the execution of code, while on the other hand async await isn't bound to any timer, to understand simply, function or promise that uses async await will wait until the function or promise returns an appropriate response...

Async / await vs then which is the best for performance?

I have a simple code in JavaScript that execute a request in an API and return the response, simple. But in this case I will have thousands of requests. So, which one of the code options will perform better, and why. Also which one is recommended as good pratices these days?
First options is using the .then to resolve the promises and the seccond one is using async / await.
In my tests the two options had very similar results without significant differences, but I'm not sure in scale.
// Using then
doSomething(payload) {
const url = 'https://link-here/consultas';
return this.axios.get(url, {
params: {
token: payload.token,
chave: payload.chave,
},
}).then(resp => resp.data);
}
// Using Async / await
async doSomething(payload) {
const url = 'https://link-here/consultas';
const resp = await this.axios.get(url, {
params: {
token: payload.token,
chave: payload.chave,
},
});
return resp.data;
}
Any explanation will be of great value.
From a performance point of view, await is just an internal version of .then() (doing basically the same thing). The reason to choose one over the other doesn't really have to do with performance, but has to do with desired coding style or coding convenience. Certainly, the interpreter has a few more opportunities to optimize things internally with await, but its unlikely that should be how you decide which to use. If all else was equal, I would choose await for the reason cited above. But, I'd first choose which made the code simpler to write and understand and maintain and test.
Used properly, await can often save you a bunch of lines of code making your code simpler to read, test and maintain. That's why it was invented.
There's no meaningful difference between the two versions of your code. Both achieve the same result when the axios call is successful or has an error.
Where await could make more of a convenience difference is if you had multiple successive asynchronous calls that needed to be serialized. Then, rather than bracketing them each inside a .then() handler to chain them properly, you could just use await and have simpler looking code.
A common mistake with both await and .then() is to forget proper error handling. If your error handling desire in this function is to just return the rejected promise, then both of your versions do that identically. But, if you have multiple async calls in a row and you want to do anything more complex than just returning the first rejection, then the error handling techniques for await and .then()/.catch() are quite different and which seems simpler will depend upon the situation.
There should be some corrections in this thread. await and .then are going to give very different results, and should be used for different reasons.
await will WAIT for something, and then continue to the next line. It's also the simpler of the two because it behaves mechanically more like synchronous behavior. You do step #1, wait, and then continue.
console.log("Runs first.");
await SomeFunction();
console.log("Runs last.");
.then splits off from the original call and starts operating within its own scope, and will update at a time the original scope cannot predict. If we can put semantics aside for a moment, it's "more asynchronous," because it leaves the old scope and branches off into a new one.
console.log("Runs first.");
SomeFunction().then((value) => {console.log("Runs last (probably). Didn't use await on SomeFunction().")})
console.log("Runs second (probably).");
As more explanation to #user280209 answer let's consider the following function which returns promise and compare its execution with .then() and async await.
function call(timeout) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`This call took ${timeout} seconds`);
resolve(true);
}, timeout * 1000);
});
}
With .then()
(async () => {
call(5).then((r) => {
console.log(r);
});
await call(2); //This will print result first
await call(1);
})();
When running the above call the logs will be
This call took 2 seconds
This call took 1 seconds
This call took 5 seconds
true
As we can see .then() didn't pause the execution of its below line until it completes.
With async/wait
(async () => {
await call(5); //This will print result first
await call(2);
await call(1);
})();
When run the above function logs will be
This call took 5 seconds
This call took 2 seconds
This call took 1 seconds
So I think if your promise's result won't be used in the following lines, .then() may be better.
For those saying await blocks the code until the async call returns you are missing the point. "await" is syntactic sugar for a promise.then(). It is effectively wrapping the rest of your function in the then block of a promise it is creating for you. There is no real "blocking" or "waiting".
run();
async function run() {
console.log('running');
makePromises();
console.log('exiting right away!');
}
async function makePromises() {
console.log('make promise 1');
const myPromise = promiseMe(1)
.then(msg => {
console.log(`What i want to run after the promise is resolved ${msg}`)
})
console.log('make promise 2')
const msg = await promiseMe(2);
console.log(`What i want to run after the promise is resolved via await ${msg}`)
}
function promiseMe(num: number): Promise<string> {
return new Promise((resolve, reject) => {
console.log(`promise`)
resolve(`hello promise ${num}`);
})
}
The await line in makePromises does not block anything and the output is:
running
make promise 1
promise
make promise 2
promise
exiting right away!
What i want to run after the promise is resolved hello promise 1
What i want to run after the promise is resolved via await hello promise 2
Actually.
Await/Async can perform more efficiently as Promise.then() loses the scope in which it was called after execution, you are attaching a callback to the callback stack.
What it causes is: The system now has to store a reference to where the .then() was called. In case of error it has to correctly point to where the error happens, otherwise, without the scope (as the system resumed execution after called the Promise, waiting to comeback to the .then() later) it isn't able to point to where the error happened.
Async/Await you suspend the exection of the method where it is being called thus preserving reference.
If we just consider performance(time taken) then it actually depends on whether your operations are serial or parallel. If your tasks are serial then there will be no difference between await and .then. But if your tasks are parallel then .then will take less time. Consider the following example
let time = Date.now();
// first task takes 1.5 secs
async function firstTask () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
},1500)
})
}
// second task takes 2 secs
async function secondTask () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
},2000)
})
}
// using await
async function call(){
const d1 = await firstTask();
const d2 = await secondTask();
console.log(Date.now()-time, d1+d2)
}
call()
// using .then
async function call2(){
let d1=null,d2=null;
firstTask().then(data => {
d1=data;
if(d2){
console.log(Date.now()-time, d1+d2);
}
})
secondTask().then(data => {
d2=data;
if(d1){
console.log(Date.now()-time, d1+d2);
}
})
}
call2()
Here are the two tasks, first takes 1.5 secs and second takes 2 secs. Call function uses await where as call2 function uses .then . The output is as follows
From call2 2012 3
From call 3506 3
I hope it helps.
As far as I understand .then() and await are not the same thing. An async function won't proceed with the next command until the promise is resolved/rejected since it's basically an implementation of generators. On the contrast, in the case of .then(), the execution of the function will proceed with the next command and the resolve/reject callback will be executed "when there's time" aka when the current event loop (not entirely sure about that part) will be completed.
tldr; on a single promise await and .then() behave similarly but when one promise needs another one to be resolved first then the two of them behave entirely different
Many answer have been provided to this question already. However, to point out key information in the answers above and from my understanding, note below point:
only use await when not handling error return
if no crucial need for error handling use await instead
use .then .catch if returned error message or data is crucial for debugging / or proper error handling instead of try catch for await
Choose any prefer method from code sample below
const getData = (params = {name: 'john', email: 'ex#gmail.com'}) => {
return axios.post(url, params);
}
// anywhere you want to get the return data
// using await
const setData = async () => {
const data = await getData();
}
// to handle error with await
const setData = async () => {
try {
const data = await getData();
}
catch(err) {
console.log(err.message);
}
}
// using .then .catch
const setData = () => {
var data;
getData().then((res) => {
data = res.data; console.log(data)
}).catch((err) => {
console.log(err.message);
});
}

Why Does Async Always Return a Promise?

This question is theoretical - I have no concrete problem to solve.
With that said, why does the async keyword wrap the return value of an async function in a promise? What's the point? Is it ONLY because the await expression expects a promise? Or is there some meaning / use behind this decision?
I thought i'd answer this primarily because async in Javascript used to confuse the hell out of me, and all of a sudden it snapped, so i hope this analogy may help this happen for you.
You have an async event. This could be anything, getting something from a server, doing something in the browser that takes time, training a machine learning model (!), executing a function or method that uses a setTimeout etc.
The beauty of Javascript and a key reason it works so well for the browser is that it uses the processor thread it runs on in a very clever way that stops the thread from getting blocked by processes that take time (like the ones mentioned above)
Many other languages, for example Ruby run on more than one thread. It is possible to use service workers to run processes on multiple threads in javascript but that is outside the scope of this answer!
The async nature of the JS event loop allows the thread to 'go off' and do something else while it is waiting for a process to finish.
The problem with this from a programming point of view is that it is possible for something in the code that relies on the result of a blocking event to get 'undefined' as a result of the event if it doesn't wait for the event to finish before it tries to use the result of it. Take this piece of code below
let scopedVariable
console.log('the code has started')
setTimeout(() => {
scopedVariable="I am the result of some async process"
}, 5000);
console.log(scopedVariable)
When the code reaches the console log, the setTimeout hasn't yet completed. As the setTimeout sets the scopedVariable only when it completes, the variable is undefined when we log it
if however
We wrap the timeout in a promise we can await it's resolve callback (first argument of promise) and the code will 'pause' until the promise reaches the resolve callback before continuing.
When we await the promise and the setTimeout completes, the resolve function sets the variable, so that when we console log it it holds the value from the promise
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
const container = async () => {
const result = await asyncEvent
console.log(scopedVariable)
}
container()
You can use await and .then interchangably
For example we could go:
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
const container = async () => {
asyncEvent.then(() => console.log(scopedVariable))
}
container()
once again the code will pause at .then and then continue when the asyncEvent promise has resolved.
In fact if we use .then we don't need to enclose it in an async function so we can rewrite it like this
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
asyncEvent.then(() => console.log(scopedVariable))
The great thing about .then is that the accompanying .catch allows you to catch any errors thrown by the async event (for example if retrieving something from a server when there is an error). For async await you need to wrap potentially dangerous functions in a try catch.
In order to use await you need to be inside an async function (hence the async container function above). This is not necessary with .then, but .then and .catch chains can make your code messy.
I hope this helps!
The async and await operators are just syntactic sugar that hide the underlying use of promises to implement asynchronous code.
Using async before a function definition makes the function return a promise that resolves to the function's return value, rather than returning normally.
Using await before an asynchronous function call suspends the current function until the promise that it returns is resolved. It's basically equivalent to wrapping the remainder of the function in an anonymous function, and using that as the .then() callback of the promise.
For more information between the relationship, see How to translate Promise code to async await

Timing when converting promise to async await

I have the following block
return this
.configure(config => {
viewModel.configure(config, this);
return config;
})
.then(() => {
this.activate();
});
which, is equivalent to following block, suggested by vscode auto promise->async/await conversion:
await this.configure(config => {
viewModel.configure(config, this);
return config;
});
this.activate();
My question is are they actually the same in timing? In the promise example, shouldn't the second fn in then() 1 micro task away from the callback that accepts config?
Extra question for clarity: are the following equal in timing perspective:
Promise.all(tasks)
.then(() => {
othertasks.forEach(() => doStuff());
})
.then(() => {
prune();
});
and:
await Promise.all(tasks);
othertasks.forEach(() => doStuff();
await Promise.resolve();
prune();
EDIT: I should clarify, with regards to Andrea Giammarchi's answer, that my answer was purely and only related to the difference in number of ticks in-between the synchronously executed code, not whether the result of that code as-written is actually equivalent (obviously, the non-awaited promises would yield promises whereas the awaited promises would yield the values)
This would make more sense in the context that me and bigopon had a discussion in a github issue where he accepted VS Code's suggestion to remove a "redundant" .then from a promise chain in a piece of legacy code that happens to be sensitive to subtle timing issues.
I pointed out that this change would cause a particular method to be executed one tick earlier, and that the effects of that could potentially break complex apps relying on these (quirky) timings.
The discussion was then, whether this:
somePromise.then(() => {
...
}).then(() => {
doStuff();
})
Would have the same timings as this:
await somePromise;
doStuff();
To which my answer was: no, the doStuff() in the second snippet would execute one tick earlier.
When someone else suggested that await or .then would actually be executed synchronously if the passed-in promise was already resolved, that motivated me to write this answer and clarify why not.
I do realize that without this context, my answer can seem misleading, but again: it's just to point out the similarity in number of ticks.
Original answer
Example 1
For resolving a value, in plain terms, this:
await something
Is equivalent to this:
Promise.resolve(something).then()
They both result in a pending promise.
Example 2
For queueing a task, this:
await Promise.resolve();
doStuff();
Is equivalent to this:
Promise.resolve().then(() => {
doStuff();
})
In both cases, doStuff() happens on the next tick.
In order to determine whether a regular .then chain is equivalent to a series of awaits, you simply need to count .then and await. If the number of each is the same between two given pieces of code, then the amount of time/ticks/whatever passing between those pieces of code will be the same.
Example 3
Another example, this:
await Promise.resolve();
doStuff();
await Promise.resolve();
doStuff();
await Promise.resolve();
await Promise.resolve();
doStuff();
Is equivalent to this:
Promise.resolve()
.then(() => {
doStuff();
})
.then(() => {
doStuff();
})
.then(() => {})
.then(() => {
doStuff();
})
Note that the Promise.resolve() itself has no effect on the timings. It returns a resolved promise. It's the then() / await that turns it into a pending one.
So I respectfully disagree with amadan and I believe both your examples are equivalent.
What the spec says
If promise.[[PromiseState]] is "pending", then
a. Append fulfillReaction as the last element of the List that is promise.[[PromiseFulfillReactions]].
b. Append rejectReaction as the last element of the List that is promise.[[PromiseRejectReactions]].
Else if promise.[[PromiseState]] is "fulfilled", then
a. Let value be promise.[[PromiseResult]].
b. Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « fulfillReaction, value »).
What this says is "if the promise is already pending, simply append the fulfillReaction of that pending promise, but of the promise is fulfilled, then enqueue a new job".
In other words, .then is guaranteed to return a pending promise regardless of whether the promise it is chained on was fulfilled or not.
I think there is quite some confusion in both what happened with VSCode, what you asked as question, and the kind of answer you received.
I'll try to clarify all points as I can, hoping I got the question right.
Let me start saying that ...
Those two blocks are not equivalent
The following piece of code:
this
.configure(config => {
viewModel.configure(config, this);
return config;
})
.then(value => `some ${value}`);
is "equivalent" only to this one:
await this
.configure(config => {
viewModel.configure(config, this);
return config;
})
.then(value => `some ${value}`);
That is because await has less priority than method chaining/then concatenation.
(async function (){
const then = await Promise.resolve(1).then(one => one + one);
console.log(then); // this is number 2
const something = await 123;
console.log(something); // this is number 123
}());
The reason you are rightly confused is that VSCode outsmarted your intent.
return this
.configure(config => {
viewModel.configure(config, this);
// configure() returns a Promise
// and config can be a Promise too
return config;
})
.then(() => {
// but you are not using here the config value
// or, if it was a promise, whatever value it resolved
// and you are also not returning any value
this.activate();
});
Since VSCode knows that configure is thenable, and its returned value could also be a Promise, which would imply the activate can happen only after config is eventually resolved, it also knows having an extra tick would make no sense because you don't need whatever config returned, either as value or promise, so that activate can be called right away.
Since you were also not returning any value in the last then, the whole return can be dropped.
// only async to wait for
await this.configure(config => {
viewModel.configure(config, this);
return config;
});
// with config either value or promise
// there's nothing else to wait for, so
// let's invoke activate without returning
// anything, producing is the same undefined result
this.activate();
To recap what happens inside that await:
(async function (){
const config = new Promise(res => setTimeout(res, 1000));
console.time('awaiting');
const value = await Promise.resolve(1).then(() => {
return config;
});
console.timeEnd('awaiting');
// awaiting: 1000.XXXms
}());
If you were by any chance using the returned value inside that last then, you would've seen that VSCode could not have dropped it, most-likely readdressed as const value = await ...; this.activate(value); which is also still OK.
To the previous comment stating:
For resolving a value, in plain terms, this:
await something
Is equivalent to this:
Promise.resolve(something).then()
They both result in a pending promise.
Not sure I read that wrong but that that felt to me quite a misleading statement.
const resolved = await anything means resolved is always a value, never a pending promise.
That's quite possibly the whole point of await: it won't stop awaiting until there is a value.
Example:
(async function (){
const something = Promise.resolve(Math.random());
// this logs the random number as typeof number
console.log(await something);
// this also logs the random number as typeof number
console.log(await Promise.resolve(something).then());
// while this is one is the only pending promise
console.log(Promise.resolve(something).then());
}());
The reason you eventually see Pending promise in console is that the AIIFE (Asynchronous Immediately Invoked Function Expression) is a promise itself and you can await it elsewhere.
You can see that returning a value or a pending promise will always produce the expected result.
(async function (){
// instant return, it's just fine
// return 123;
// return promise (unnecessary ticks added)
return Promise.resolve(123).then();
}()).then(console.log);
In both cases the 123 number is logged.
I hope it's clear now, specially for the OP, what happened in VSCode, and, specially, why happened.
Regards.

Categories