According to MDN Web Docs, "await" is used to "wait for a Promise", but the
expression following the keyword can also be "any value to wait for", hence not
necessarily a Promise.
In the below demo the awaited value is a call to a void function, i.e. a
function that implicitly returns "undefined". Note: the called function does not
return a Promise. What exactly does it mean to await a void function call?
Demo:
Add keyword "await" to the beginning of line 11, and see how it changes logging
from "FC" to "CF".
"use strict";
function pr() {
return Promise.resolve(null);
}
function invokeAsyncOperation(cb) {
pr().finally(cb);
}
async function main() {
try {
// demo: await the following statement
invokeAsyncOperation(() => console.log("C"));
} catch (e) {} finally {
console.log("F");
}
}
main();
You're seeing the result of two things:
From the docs you linked:
If the value of the expression following the await operator is not a Promise, it's converted to a resolved Promise.
Loosely speaking,¹ await x where x is not a promise is the same as await Promise.resolve(x). So when you use await, there's always a promise involved.
Promise completion handlers are always called asynchronously, x and await x have different timing: The former is synchronous; the latter is asynchronous:
A promise is created, fulfilled with x.
await puts a completion handler on the promise.
Since the promise is already fulfilled, a call to that completion handler is put in the microtask queue to be handled when microtasks are run at the end of the current task.
When microtasks are run, the promise completion is reached and does the console.log.
You see the log for C before the log for F because the completion handler logging C is already in the microtask queue before the completion handler for F is added, so it gets run first.
¹ Actually, Promise.resolve is always involved, even when x is already a promise. But the extra promise gets optimized away if x is already a native promise (of the same concrete class). You can see the resolve operation in Step 2 of the algorithm for await.
Related
I'm pretty new to JavaScript (using Node.js) and still learning. I try to wrap around my head with promises and I got into this situation where I don't understand if there is a difference between this code:
promiseFunc().then(() => {
anotherPromiseFunc() // I dont need .then() here, just want to save some data to DB
});
doSmthElse()
and this code:
promiseFunc().then(async () => {
await anotherPromiseFunc() // I dont need .then() here, just want to save some data to DB
});
doSmthElse()
If I don't use .then() in the first case, does it mean there is a possibility that doSmthElse() will be executed before anotherPromiseFunc() is executed? So in order to prevent that, I have to add async/await? Or all code in .then() block is being waited to execute anyway before doing something else?
Note 1: I don't want to chain those promises, because there is more code in my case, but I just simplified it here.
Note 2: I don't use catch because if error will rip through I will catch it later.
If I don't use .then() in the first case, does it mean there is a possibility that doSmthElse() will be executed before AnotherPromise() is executed?
doSmthElse() is guaranteed to be executed before anything in the fulfillment handler¹ is executed. Promise fulfillment and rejection handlers are always invoked asynchronously. That's true whether you declare the handler function using async or not.
For example:
console.log("before");
Promise.resolve(42)
.then(result => {
console.log("within", result);
});
console.log("after");
The output of that is:
before
after
within 42
So in order to prevent that, I have to add async/await?
Where you've added async/await in your example doesn't change when doSmthElse() is executed.
If you want doSmthElse() to wait until the promise has been fulfilled, move it into the fulfillment handler.¹
If your code were inside an async function, you could use await instead, like this:
// Inside an `async` function
await promiseFunc().then(() => {
anotherPromiseFunc();
});
doSmthElse()
That would do this:
Call promiseFunc() and get the promise it returns
Hook up a fulfillment handler to that promise via then, returning a new promise; that new promise is the operand to await
Wait for the promise from #1 to settle
When it does, your fulfillment handler is executed and runs anothterPromiseFunc() but doesn't wait for the promise it returns to settle (because, as you said, you're not returning that promise from the fulfillment handler).
At this point, the promise from #2 is fulfilled because your fulfillment handler has (effectively) returned undefined, which isn't a thenable (loosely, a promise), so that value (undefined) is used to fulfill the promise from #2.
Since the promise from #2 has been fulfilled, await is satisfied and doSmthElse() is executed.
¹ the function you pass then as its first argument
I assume that Promise() just stands for some function call returning a Promise:
You could say that .then registers an event listener that runs as soon as the promise settles => the task is finished. It still runs asynchronously So in your example doSmthElse will still run even if the promise hasn't been settled (so if the promise doesn't settle immediately doSmthElse will be called before the function inside .then)
To let your code run "in order". You would have to use await to ensure that doSmthElse is called after the promise settled or you could put doSmthElse into the .then block.
This question already has answers here:
Will async/await block a thread node.js
(6 answers)
Closed 3 years ago.
I have two functions, the first is the main function which has switch statement calling the second which is async. If my second function is async isn't that non-blocking? Would I still have to make the first one async in order for it to be non-blocking, if so why?
Example
exports.funcOne = async (theParam) => { // async ??
switch (theParam) {
case 'hey':
return await funcTwo()
default:
...
}
}
const funcTwo = async () => {
await axios.get...
}
Second function can just return the promise :
exports.funcOne = async (theParam) => { // async ??
switch (theParam) {
case 'hey':
const myData = await funcTwo();
console.log("response from request : ", myData);
//do something with data
default:
...
}
}
const funcTwo = () => {
//axios.get is a promise, so is the return type for funcTwo
return axios.get...
}
Calling an AsyncFunction returns a Promise. As Eric said you could return the Promise in the funcTwo and it doesnt need to be an AsyncFunction because it is secuential and it is in the functionOne thread. So if you return the Promise in the functionTwo, the result of the Promise "axios.get..." will be resolved in "return await funcTwo()" in the functionOne.
In general, all functions that contain await must be async.
That rule might make more sense if we imagine it would not exist, and if you could wait for an asynchronous result in a synchronous function.
First of all, we need to know one important things about functions:
They have to run to completion, or in other words: A regular function runs till it's return and no other code can run at the same time.
If we could await inside of a non-async function that would mean that we would block execution until the awaited promise resolves, as no other code can run in the meantime. As the code that resolves the promise also cannot run, we created a deadlock.
async functions only run to completion till they reach an await. Therefore, when axios.get(...) gets called, a promise gets returned synchronously from it, funcTwo halts execution, returns a promise itself, therefore funcOne also halts execution (thats possible cause it's async). Then the engine can continue with other stuff, and somewhen when the underlying request is done, the promise resolves, funcTwo continues execution which resolves the promise it returned, funcOne continues execution and resolves the returned promise too.
If my second function is async isn't that non-blocking?
That really depends on your definition of non-blocking. In general, every code of JS that executes kind of "blocks the thread" (as only one function can execute at a time), but as most tasks are very small, you won't notice that blocking.
An asnyc function is also blocking, as long as it doesn't halt for a promise to resolve. Then it is in a non-blocking waiting state.
Asynchronous tasks (e.g. doing a network request) also aren't blocking, cause they are handled by the engine (offloaded to other threads or hardware, etc.).
I'm playing with Promise Extensions for JavaScript (prex) and I want to extend the standard Promise class with cancellation support using prex.CancellationToken, complete code here.
Unexpectedly, I'm seeing the constructor of my custom class CancellablePromise being called twice. To simplify things, I've now stripped down all the cancellation logic and left just a bare minimum required to repro the issue:
class CancellablePromise extends Promise {
constructor(executor) {
console.log("CancellablePromise::constructor");
super(executor);
}
}
function delayWithCancellation(timeoutMs, token) {
// TODO: we've stripped all cancellation logic for now
console.log("delayWithCancellation");
return new CancellablePromise(resolve => {
setTimeout(resolve, timeoutMs);
}, token);
}
async function main() {
await delayWithCancellation(2000, null);
console.log("successfully delayed.");
}
main().catch(e => console.log(e));
Running it with node simple-test.js, I'm getting this:
delayWithCancellation
CancellablePromise::constructor
CancellablePromise::constructor
successfully delayed.
Why are there two invocations of CancellablePromise::constructor?
I tried setting breakpoints with VSCode. The stack trace for the second hit shows it's called from runMicrotasks, which itself is called from _tickCallback somewhere inside Node.
Updated, Google now have "await under the hood" blog post which is a good read to understand this behavior and some other async/await implementation specifics in V8.
Updated, as I keep coming back to this, adding static get [Symbol.species]() { return Promise; } to the CancellablePromise class solves the problem.
First Update:
I first thought .catch( callback) after 'main' would return a new, pending promise of the extended Promise class, but this is incorrect - calling an async function returns a Promise promise.
Cutting the code down further, to only produce a pending promise:
class CancellablePromise extends Promise {
constructor(executor) {
console.log("CancellablePromise::constructor");
super(executor);
}
}
async function test() {
await new CancellablePromise( ()=>null);
}
test();
shows the extended constructor being called twice in Firefox, Chrome and Node.
Now await calls Promise.resolve on its operand. (Edit: or it probably did in early JS engine's versions of async/await not strictly implemented to standard)
If the operand is a promise whose constructor is Promise, Promise.resolve returns the operand unchanged.
If the operand is a thenable whose constructor is not Promise, Promise.resolve calls the operand's then method with both onfulfilled and onRejected handlers so as to be notified of the operand's settled state. The promise created and returned by this call to then is of the extended class, and accounts for the second call to CancellablePromise.prototype.constructor.
Supporting evidence
new CancellablePromise().constructor is CancellablePromise
class CancellablePromise extends Promise {
constructor(executor) {
super(executor);
}
}
console.log ( new CancellablePromise( ()=>null).constructor.name);
Changing CancellablePromise.prototype.constructor to Promise for testing purposes causes only one call to CancellablePromise (because await is fooled into returning its operand) :
class CancellablePromise extends Promise {
constructor(executor) {
console.log("CancellablePromise::constructor");
super(executor);
}
}
CancellablePromise.prototype.constructor = Promise; // TESTING ONLY
async function test() {
await new CancellablePromise( ()=>null);
}
test();
Second Update (with huge thanks to links provided by the OP)
Conforming Implementations
Per the await specification
await creates an anonymous, intermediate Promise promise with onFulilled and onRejected handlers to either
resume execution after the await operator or throw an error from it, depending on which settled state the intermediate promise achieves.
It (await) also calls then on the operand promise to fulfill or reject the intermediate promise. This particular then call returns a promise of class operandPromise.constructor. Although the then returned promise is never used, logging within an extended class constructor reveals the call.
If the constructor value of an extended promise is changed back to Promise for experimental purposes, the above then call will silently return a Promise class promise.
Appendix: Decyphering the await specification
Let asyncContext be the running execution context.
Let promiseCapability be ! NewPromiseCapability(%Promise%).
Creates a new jQuery-like deferred object with promise, resolve and reject properties, calling it a "PromiseCapability Record" instead. The deferred's promise object is of the (global) base Promise constructor class.
Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
Resolve the deferred promise with the right operand of await. The resolution process either calls the then method of the operand if it is a "thenable", or fulfills the deferred promise if the operand is some other, non-promise, value.
Let stepsFulfilled be the algorithm steps defined in Await Fulfilled Functions.
Let onFulfilled be CreateBuiltinFunction(stepsFulfilled, « [[AsyncContext]] »).
Set onFulfilled.[[AsyncContext]] to asyncContext.
Create an onfulfilled handler to resume the await operation, inside the async function it was called in, by returning the fulfilled value of the operand passed as argument to the handler.
Let stepsRejected be the algorithm steps defined in Await Rejected Functions.
Let onRejected be CreateBuiltinFunction(stepsRejected, « [[AsyncContext]] »).
Set onRejected.[[AsyncContext]] to asyncContext.
Create an onrejected handler to resume the await operation, inside the async function it was called in, by throwing a promise rejection reason passed to the handler as its argument.
Perform ! PerformPromiseThen(promiseCapability.[[Promise]], onFulfilled, onRejected).
Call then on the deferred promise with these two handlers so that await can respond to its operand being settled.
This call using three parameters is an optimisation that effectively means then has been called internally and won't be creating or returning a promise from the call. Hence settlement of the deferred will dispatch calling one of its settlement handlers to the promise job queue for execution, but has no additional side effects.
Remove asyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
Set the code evaluation state of asyncContext such that when evaluation is resumed with a Completion completion, the following steps of the algorithm that invoked Await will be performed, with completion available.
Store where to resume after a successful await and return to the event loop or micro task queue manager.
i.e.
async asyncfunction(){
try{
await method1();
await method2();
}
catch(error){
console.log(error);
}
}
Given method1() and method2() are asynchronous functions. Should there be a try/catch block for each await method? Is there an even cleaner way to write this? I'm trying to avoid '.then' and '.catch' chaining.
Using one try/catch block containing multiple await operations is fine when waiting for promises created on the right hand side of the await unary operator:
The await operator stores its parent async functions' execution context and returns to the event loop. Execution of the await operator resumes when it is called back with the settled state and value of its operand.
Upon resumption, await restores the previously saved execution context and returns the operand promise's fulfilled value as the result of the await expression, or throws the rejection reason of a rejected operand.
The try/catch block invocation is part of the execution context both before and after being saved and restored. Hence multiple await operations do not disturb the behavior of an outer try block they share. The catch block will be invoked with the rejection reason of any promise awaited in the try block that is rejected.
If however code awaits multiple existing promises in the same try/catch block and more than one of the promises rejects, an uncaught promise rejection error is generated for all but the first rejection. Thanks to #EyolRoth for supplying this caveat, please read his entire answer in conjunction with this one.
While #traktor answer is correct, I want to add a very important caveat.
The following code snippet is unsafe:
async function foo() {
try {
const p1 = method1();
const p2 = method2();
await p1;
await p2;
} catch (e) {
console.log(e);
}
}
If both promises are rejected, the code will result in UnhandledPromiseRejectionWarning in NodeJS, potentially killing the process if the option --unhandled-rejections is set to throw (which will become the default behavior from Node 15).
Why is this happening?
In the original code of the question, the async methods are invoked sequentially; i.e, if the first method fails (promise rejected), the second method is never invoked.
However, the code from the beginning of this answer invokes both of the async methods in parallel; i.e, the second method is invoked regardless if the first method succeeds or not.
In this scenario, only the await on the promise of the second method is conditioned on the success of the first method. Meaning, if the first method fails, the promise of the second method (which has already started) is never awaited, so if it fails too, it will result in an handled promise rejection.
How to overcome this behavior?
Assuming we wish to handle both rejections in the same way, we can use Promise.all:
try {
const p1 = method1();
const p2 = method2();
await Promise.all([p1, p2]);
} catch (e) {
console.log(e);
}
Only the first promise to be rejected will "trigger" the catch clause, while the second will be silently ignored without crashing the process.
Using Async.js Library
Let's talk about working with the async.js library in order to avoid callback hell.
As per the official website of async.js : Async is a utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript.
Async.js provides near about 70 functions in total. For now, we will discuss only two of them i.e, async.waterfall() and async.series().
async.waterfall()
This method is useful when you want to run some tasks one after the other and then pass the result from the previous task to the next task. It takes an array of functions "tasks" and a final "callback" function that is called after all functions in "tasks" array have completed or a "callback" is called with an error object.
async.js library
I would like to get a deeper understanding of how Promises work internally.
Therefore I have some sample code:
var p1 = new Promise(
function(resolve, reject) {
window.setTimeout(
function() {
resolve('res called')
}, 2000);
});
var p2 = new Promise(
function(resolve, reject) {
window.setTimeout(
function() {
resolve('res called')
}, 2000);
});
function chainPromises() {
return p1.then(function(val) {
console.log("p1");
return p2.then(function(val) {
console.log("p2");
return val;
});
});
}
chainPromises().then(function(val) {
console.log(val);
});
Here a link to execute this code.
As you would predict, first p1 is resolved, afterwards p2 and in the end the final then prints the resolv value.
But the API ref states the following:
"then" returns a new promise equivalent to the value you return from
onFulfilled/onRejected after being passed through Promise.resolve
So it would be interesting to know WHEN exactly the "then" function is executed?
Because the final "then" in the code is chained to the chainPromises(), I first thought that
it would execute after the function chainPromises() returns something (in this case another promise).
If this would have been the case the "val" of the final "then" function would be the returned promise.
But instead, the final "then" waits until all promises inside the first "then" which are returned have been resolved.
This absolutely makes sense because in this way, the "then" functions can be stacked, but
I do not really get how this is done, since the API spec. does not really cover what "then" returns and when the "then" functions is executed.
Or in other words, why does the final "then" function wait until all the Promises are resolved inside the chainPromises() function instead of just waiting for the first returned object as the API doc says.
I hope I could make clear what I mean.. :)
About Promise resolution
The thing you're witnessing here is called recursive thenable resolution. The promise resolution process in the Promises/A+ specification contains the following clause:
onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x)
The ES6 promise specification (promises unwrapping) contains a similar clause.
This mandates that when a resolve operation occurs: either in the promise constructor, by calling Promise.resolve or in your case in a then chain a promise implementation must recursively unwrap the returned value if it is a promise.
In practice
This means that if onFulfilled (the then) returns a value, try to "resolve" the promise value yourself thus recursively waiting for the entire chain.
This means the following:
promiseReturning().then(function(){
alert(1);
return foo(); // foo returns a promise
}).then(function(){
alert(2); // will only run after the ENTIRE chain of `foo` resolved
// if foo OR ANY PART OF THE CHAIN rejects and it is not handled this
// will not run
});
So for example:
promiseReturning().then(function(){
alert(1);
return Promise.resolve().then(function(){ throw Error(); });
}).then(function(){
alert("This will never run");
});
And that:
promiseReturning().then(function(){
alert(1);
return Promise.resolve().then(function(){ return delay(2000); });
}).then(function(){
alert("This will only run after 2000 ms");
});
Is it a good idea?
It's been the topic of much debate in the promises specification process a second chain method that does not exhibit this behavior was discussed but decided against (still available in Chrome, but will be removed soon). You can read about the whole debate in this esdiscuss thread. This behavior is for pragmatic reasons so you wouldn't have to manually do it.
In other languages
It's worth mentioning that other languages do not do this, neither futures in Scala or tasks in C# have this property. For example in C# you'd have to call Task.Unwrap on a task in order to wait for its chain to resolve.
Let's start with an easy perspective: "chainPromises" returns a promise, so you could look at it this way:
// Do all internal promises
var cp = chainPromises();
// After everything is finished you execute the final "then".
cp.then(function(val) {
console.log(val);
});
Generally speaking, when returning a promise from within a "then" clause, the "then" function of the encapsulating promise will be marked as finished only after the internal "then" has finished.
So, if "a" is a promise, and "b" is a promise:
// "a"'s "then" function will only be marked as finished after "b"'s "then" function has finished.
var c = a.then(function () {
return b.then(function () {
console.log("B!");
};
};
// c is a promise, since "then" always returns a promise.
c.then(function() {
console.log("Done!");
};
So the output will be:
B!
Done!
Notice btw, that if you don't "return" the internal promise, this will not be the case:
// "a"'s "then" function will only be marked as finished without waiting for "b"'s "then" to finish.
var c = a.then(function () {
// Notice we're just calling b.then, and don't "return" it.
b.then(function () {
console.log("B!");
};
};
// c is a promise, since "then" always returns a promise.
c.then(function() {
console.log("Done!");
};
Here we can't know what would be outputted first. It could be either "B!" or "Done!".
Please check the below example regarding how promises works:
The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
console.log('person1: shoe ticket');
console.log('person2: shoe ticket');
const promiseGirlFriendBringingTickets = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ticket');
}, 3000);
});
promiseGirlFriendBringingTickets.then((t) => {
console.log(`person3: show ${t}`);
})
console.log('person4: shoe ticket');
console.log('person5: shoe ticket');
Promise then return promise object, not promise's resolved value. I forked your JsFiddle, and added some of mine try this.
promise.then is executed right after that promise object is resolved.
I do not know how this is done in actual promises libraries, but I was able to re-create this functionality in the following way:
1) each promise has a waitingPromises property;
2) then method returns a new promise, and the original promise's waitingPromises property points to the new promise.
In this way, the chain of .then()s creates a structure that is similar to a linked list or rather a tree (each promise can have several waiting promises). A promise can be resolved only after its 'parent' promise has been resolved. The .then method itself is executed immediately, but the corresponding promise that it creates is resolved only later.
I am not sure this is a good explanation and would love to learn about other possible approaches.
Normally code is synchronous - one statement executes like (fileopen) and there is a guarantee that the next statement will execute immediately afterwards like filewrite()
but in asynchronous operations like nodejs, you should assume that
you have no idea when the operation will complete.
You can't even assume that just because you send out one request first, and another request second, that they will return in that order
Callbacks are the standard way of handling asynchrnous code in JavaScript
but promises are the best way to handle asynchronous code.
This is because callbacks make error handling difficult, and lead to ugly nested code.
which user and programmer not readble easily so promises is the way
You can think of Promise as a wrapper on some background task. It takes in a function which needs to be executed in the background.
The most appropriate place to use a promise is where some code is dependent on some background processing and it needs to know the status of the background task which was executed. For that, the background task itself accepts two callback resolve and reject in order to convey its status to the code which is dependent on it. In layman terms, this code is the one behind it in the promise chain.
When a background task invokes resolve callback with some parameter. it's marking the background operation successful and passing the result of the background operation to the next then block which will be executed next. and if it calls reject, marking it as unsuccessful then the first catch block will be executed.
In your custom promise, you can pass an error obj to the reject callback so that next catch block is aware of the error happened in the background task.