Is the Promise Constructor Callback Executed Asynchronously

Is the Promise constructor callback executed asynchronously?

It depends on the implementation of the promise. If we check the spec. You can find the final spec here - since this answer was originally written, it has been finalized.

Here is the relevant excerpt (you can find the original source here)


  1. Let completion be Call(executor, undefined, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»).
  2. If completion is an abrupt completion, then

    • Let status be Call(resolvingFunctions.[[Reject]], undefined, «completion.[[value]]»).
    • ReturnIfAbrupt(status).

The ES6 standard indicates that the fulfillment of a promise is always asynchronous (See section 25.4.5.3, Promise.prototype.then and accompanying section 25.4.5.3.1, PerformPromiseThen). I have placed the relevant material below.

PerformPromiseThen


  1. Else if the value of promise's [[PromiseState]] internal slot is "fulfilled",

    • Let value be the value of promise's [[PromiseResult]] internal slot.
    • Perform EnqueueJob("PromiseJobs", PromiseReactionJob, «fulfillReaction, value»).
  2. Else if the value of promise's [[PromiseState]] internal slot is "rejected",

    • Let reason be the value of promise's [[PromiseResult]] internal slot.
    • Perform EnqueueJob("PromiseJobs", PromiseReactionJob, «rejectReaction, reason»).

TLDR: the function passed to the promise is executed synchronously, but subsequent then calls are always executed asynchronously.

Javascript Promise Callback (when it is called)

The promise executor function is called immediately by the promise constructor. That's how it works. The usual point of that executor function is to initiate your asynchronous operation and then later when that asynchronous operation completes, you call resolve() or reject().

Here's an example of putting the plain callback version of fs.readFile() in a promise wrapper:

function readFilePromise(filename, options) {
return new Promise((resolve, reject) => {
fs.readFile(filename, options, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
})
});
}

Usage:

 readFilePromise("myfile.txt").then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});

Note: This is just an example for illustration here. Nodejs now already has promisified versions of fs.readFile called fs.promises.readfile, but this structure can be used when you need to manually promisify other more complicated things that don't have their own promise interface yet.


For a real world example of promisifying something more complicated see the mapConcurrent() function here or the rateLimitMap() function here.

JavaScript: Promise callback execution

The promise executor function is the function you pass to new Promise. It is executed synchronously so it can start whatever asynchronous process the promise represents.

The callbacks you attach with then, catch, and finally are always called asynchronously, whether the promise is already settled or not.

So in your example, console.log("After resolving the promise"); is called before the console.log you've passed to then.

Here's an example showing that more clearly:

let myPromise = new Promise((resolve, reject) => {    console.log("In the executor function");    resolve("Resolved from the promise");});myPromise.then(result => {    console.log("In the fulfillment handler: " + result);});
console.log("After resolving the promise");

Where does Promise executor callback live when asynchronous code is being run?

If that is the case, above code snippet's executor function has asynchronous code of making API call - will that also hang on to the main thread till API returns data.

No, it won't hang anything. request.send() is asynchronous and non-blocking. When you call request.send() it initiates the sending of the request (turns it over to the OS) and then immediately returns. The promise executor function itself returns and then the main thread is free to do whatever else it wants to do.

Meanwhile, sometime later, the underlying TCP operation will cause an event to get inserted into the event queue. When that event gets processed by the main thread (when it's not doing anything else and this event gets its turn in the event queue), the request object will then generate either an onload or an onerror event and the corresponding listener will resolve or reject the promise. That will set in motion the promise calling it's .then() or .catch() handlers on a future tick.

Using request this way is absolutely no different inside a promise executor function as it is anywhere else in node.js. It initiates the asynchronous, non-blocking operation, you set up some listeners for callbacks and then the nodejs interpreter is free to do other things until sometime later when a completion event occurs and some callbacks get called.

Where does Promise executor callback live when asynchronous code is being run?

It's not super clear what you mean by "where does it live?". The promise executor function is just like any other function. It's called synchronously and when it returns the new Promise() constructor returns and there's a promise created that the calling code can use. Sometime later that promise will get resolved or rejected (usually). Part of initializing and setting up the promise is running of the promise executor function, but that's just one step in creating the promise. That function doesn't block if you happen to do something asynchronous in it.

Promise is synchronous or asynchronous in node js

The function you pass into the Promise constructor runs synchronously, but anything that depends on its resolution will be called asynchronously. Even if the promise resolves immediately, any handlers will execute asynchronously (similar to when you setTimeout(fn, 0)) - the main thread runs to the end first.

This is true no matter your Javascript environment - no matter whether you're in Node or a browser.

console.log('start');const myProm = new Promise(function(resolve, reject) {  console.log('running');  resolve();});myProm.then(() => console.log('resolved'));console.log('end of main block');

Why does the Promise constructor require a function that calls 'resolve' when complete, but 'then' does not - it returns a value instead?

Bergi's answer is excellent, and has been very helpful to me. This answer is complementary to his. In order to visualize the relationship between the Promise() constructor and the then() method, I created this diagram. I hope it helps somebody... maybe even me, a few months months from now.

The main idea here is that the "executor" function passed to the Promise() constructor sets tasks in motion that will set the state of the promise; whereas the handlers you pass to then() will react to the state of the promise.

Diagram: Promise() executor vs. then() method
(Code examples adapted from Jake Archibald's classic tutorial.)

This is a highly simplified view of how things work, leaving out many important details. But I think if one can keep a grip on a good overview of the intended purpose, it will help avoid confusion when one gets into the details.

A couple of selected details

The executor is called immediately

One important detail is that the executor function passed to the Promise() constructor is called immediately (before the constructor returns the promise); whereas the handler functions passed to the then() method will not be called till later (if ever).

Bergi mentioned this, but I wanted to restate it without using the terms a/synchronously, which can be confused if you're not reading carefully: The distinction between a function calling something asynchronously vs. being called asynchronously is easy to gloss over in communication.

resolve() is not onFulfill()

One more detail I'd like to emphasize, because it confused me for a while, is that the resolve() and reject() callbacks passed to the Promise() constructor's executor function are not the callbacks later passed to the then() method. This seems obvious in retrospect, but the apparent connection had me spinning in circles for too long. There is definitely a connection, but it's a loose, dynamic one.

Instead, the resolve() and reject() callbacks are functions supplied by the "system", and are passed to the executor function by the Promise constructor when you create a promise. When the resolve() function is called, system code is executed that potentially changes the state of the promise and eventually leads to an onFulfilled() callback being called asynchronously. Don't think of calling resolve() as being a tight wrapper for calling onFulfill()!



Related Topics



Leave a reply



Submit