2

I need know to whether a Promise is executed synchronously or asynchronously. According to the mozilla docs, the promise callback - executor function is executed immediately by the Promise implementation.

But it does not seem to work like that to me according to the following code-

let myPromise = new Promise((resolve, reject) =>
    resolve("Resolved from the promise");
);

myPromise.then(console.log);

console.log("After resolving the promise");

The log in the promise then handler gets printed after the log on the last line. Why it is executing like asynchronous way. Is I am missing anything?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Dev Choudhary
  • 105
  • 2
  • 8
  • If you want to see the promise executor getting immediately invoked, add a `console.log` just before the call to `resolve`. – jonrsharpe Jan 21 '20 at 19:19

3 Answers3

10

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");

The output of that is:

In the executor function
After resolving the promise
In the fulfillment handler: Resolved from the promise

Notice that "In the executor function" is logged before "After resolving the promise" because the executor is called synchronously, but "In the fulfillment handler: Resolved from the promise" is afterward because that's called asynchronously.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    Thanks, but I got another question then aren't promises from the ES6 specs and we know that Javascript is all single threaded, synchronous and blocking and all async operations are part of Web APIs and not core Javascript, then how come then handler gets executed for the promises asynchronously? – Dev Choudhary Jan 21 '20 at 19:23
  • 2
    @DevChoudhary - JavaScript isn't single-threaded, but there's only one JS thread per *realm* (loosely, a global environment -- so a page or tab, for instance). But you can have web workers, which are separate threads. But the important thing is this: Being single threaded and doing things asynchronously aren't mututally exclusive. Think about `setTimeout`: It schedules an asynchronous callback...that will occur on the *same* thread. Promise resolution handlers are also asynchronous callbacks on the same thread. The engine adds them to the queue, and processes the queue on that thread. – T.J. Crowder Jan 21 '20 at 19:42
  • 2
    Moreover, *browsers* aren't single-threaded. So for example, when you're doing a `fetch` to request a network resource, the browser handles that I/O work on another thread (to the extent it needs to use a thread for it). It's only when the browser needs to send the data to the JavaScript code that the JavaScript thread needs to be involved. – T.J. Crowder Jan 21 '20 at 19:43
  • 2
    I literally just answered another question earlier today ([here](https://stackoverflow.com/a/59846882/157247)) that you may find useful as well. Happy coding! – T.J. Crowder Jan 21 '20 at 19:44
  • But isn't `setTimeout` a web API? As far as I know all asynchronous tasks are handled by web APIs and not by JavaScript. Everything in JavaScript is said to be synchronous, right? That being said how come `promise` handlers work asynchronously being part of JavaScript? – Dev Choudhary Jan 21 '20 at 20:01
  • @DevChoudhary - Yes on both fronts. `setTimeout` is a web API thing, but it (and other asynchronous operations) aren't at odds with JavaScript at all. Nothing about an asynchronous callback conflicts in any way with JavaScript's single-thread-per-realm, run-to-completion nature. And yes, promise callbacks were the first thing in JavaScript itself that was specified to be asynchronous (since then dynamic `import()` was added, which is also defined to be asynchronous). On your second question, promise settlement callbacks are asynchronous because that's the best way for them to work. ... – T.J. Crowder Jan 22 '20 at 07:39
  • ... When you do `.then(() => { foo(42); })`, it's important to know that `foo` **definitely** will not be called synchronously, whether the promise is already settled or not, rather than having it called chaotically (synchronously if the promise is settled, but asynchronously if the promise is unsettled). jQuery made the mistake of having `Deferred` call its callbacks chaotically. It was Not Good.™ :-) The [Promises/A+ spec](https://promisesaplus.com/) requires that handlers get called async because chaotic callbacks are a bad idea, and JS's promises are completely compliant with that spec. – T.J. Crowder Jan 22 '20 at 07:41
  • Okay, I know I am asking quite a number of questions, but actually wanted to get things clear, so on a side note - callbacks, in general, are executed on completion of certain routine, for example in case of `addEventListener` callback, the callback is called when the specified event is triggered. So in case of `promises`, on completion of which routine the `promise` handlers (`then`, `catch`, `finally`) callbacks are called? Wouldn't it will be helpful if they are called immediately (synchronously) after resolving or rejecting `promises`? – Dev Choudhary Jan 22 '20 at 12:59
  • @DevChoudhary - *"callbacks, in general, are executed on completion of certain routine"* Not really. *Completion* callbacks are, but even your example isn't executed when something completes, just when an event occurs. Array `sort`'s callback isn't during completion, or String's `replace` callback. But your question is why aren't promise callbacks called *synchronously* during promise settlement (fulfillment/rejection). The answer is: I can think of a couple of reasons. 1. Because that, too, would be chaotic. Promises are often settled before any handlers are attached. ... – T.J. Crowder Jan 22 '20 at 13:32
  • ... One of the guarantees of promises is that if you attach a handler in that situation, you *will* get a callback for it (assuming your handler is for the appropriate settlement). So that means *maybe* the settlement code would be calling handlers (and being delayed by their result) or maybe it wouldn't and it would be later in the code adding handlers. And if it's synchronous and in the code adding handlers, we're back to the other chaos from my comment above (sometimes your handler is called synchronously, sometimes not). 2. It would inject failure modes into the software component ... – T.J. Crowder Jan 22 '20 at 13:32
  • ... settling the promise, by delaying its code while the callback ran, by possibly having it receive an exception, and so on. Much simpler if settlement callbacks are always called asynchronously. :-) Aside from those two, doing it asynchronously has another possible advantage: Only platform code will be on the stack when a promise settlement handler is called, no userland code. I haven't specifically seen any, but I wouldn't be surprised if there were places that was useful... So all in all, it just makes good sense to keep it simple and asynchronous. – T.J. Crowder Jan 22 '20 at 13:32
  • FWIW, I go into Promises in some depth in Chapter 8 of my new book (and `async`/`await` in Chapter 9). Maybe not *quite* as much depth on this particular point as the comments above, but certainly I address the basic chaos issue. See my profile for links if you're interested. – T.J. Crowder Jan 22 '20 at 13:34
  • Thanks, and sure, I will check the book and I am actually surprised to know that `JavaScript` has started including asynchronous processes in its specs. Now I am curious to know how `promise` execution looks like in the event loop since in the event loop asynchronous processes are executed by web APIs (browser) and not the `JavaScript` call stack. Will research regarding this on the web. – Dev Choudhary Jan 24 '20 at 20:28
  • @DevChoudhary - *"...since in the event loop asynchronous processes are executed by web APIs (browser) and not the JavaScript call stack..."* They aren't. Enjoy! **:-)** – T.J. Crowder Jan 24 '20 at 21:48
3

With creating a promise, you entered the so-called microtask realm: executed not exactly in the next event loop tick, but not immediately, either. The same thing can be achieved with the queueMicrotask function. Consider:

setTimeout(() => console.log("I'll be printed last"));

Promise.resolve().then(() => console.log("I'll be printed second"));

queueMicrotask(() => console.log("I'll be printed third"));

console.log("I am printed first");

If you swap the Promise.resolve line with the queueMicrotask line, you'll swap their respective outputs, too. The set order is always: run code to completion (the immediate console.log gets executed), run any pending microtask (the second and the third line), go to the next event loop tick (which in the above example is occupied by the function call coming from setTimeout).

Further reading: https://javascript.info/event-loop

mbojko
  • 13,503
  • 1
  • 16
  • 26
1

The executor function of a Promise is always called synchronously.

So, after this line,

let myPromise = new Promise((resolve, reject) =>
    resolve("Resolved from the promise");
);

the promise will be in resolved state.

In contrast, the callbacks added with then are called always asynchronously, even if the promise was already in a settled state at the time of adding the callback.

FZs
  • 16,581
  • 13
  • 41
  • 50