19

Suppose I have this code

function y(resolve, reject)
{
  console.log("Result");
  resolve();
}  

var promise = new Promise(y);

What I want to know is whether the function y will be executed asynchronously or not.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
qnimate
  • 879
  • 1
  • 7
  • 12
  • 1
    No. `y()` will be executed synchronously. Within `y()`, calls to `setTimeout()`, AJAX requests, or similar would result in `resolve()` or `reject()` being executed asynchronously, but that's not the case in the code you posted. – Frédéric Hamidi Apr 30 '15 at 08:53
  • This is actually a good question. I almost answered it until I realized one part that I can't answer: what happens if you call `resolve()` synchronously? Will the promise still fire the `.then()`? In my own implementation of promises I made sure that promises would work both synchronously and asynchronously. But some of the specifications (I think Promise/A?) only support asynchronous `resolve()`. So what does ES6 do? – slebetman Apr 30 '15 at 08:57
  • @slebetman see [this blog post](http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony) about the dangers of making your API respond both synchronously **and** asynchronously – Dan Apr 30 '15 at 08:58
  • @DanPantry: I don't see it as relevant to promises. I see promises as a higher level abstraction of structure, not an API. We already have this in javascript. The `function` keyword can be both sync and async. Promises should behave like `function` not like `setTimeout`. If you wrap `setTimeout` in a promise then it will always be async. If you wrap `forEach` in a promise then it will always be sync. There will never be cases when the code is sometimes async and sometimes sync. – slebetman Apr 30 '15 at 09:06
  • Easy to check `new Promise(function(resolve){ console.log(1); resolve(3); }).then(console.log.bind(console)) console.log(2);` logs 1,2,3. Hence executor is called synchronously, subsequent `then` calls - not. – Yury Tarabanko Apr 30 '15 at 09:21
  • @FrédéricHamidi @slebetman @YuryTarabanko then what's the use of promises. As you said that the `y` function will be executed synchronously. We can just call `y` function directly and then the `then()` function's success callback at the end of the `y` function. why we use promises? – qnimate Apr 30 '15 at 09:32
  • 1
    Because promises compose well and provide a unified abstraction for that behaviour. Otherwise you just end up with callback soup. Just because the behaviour of the component is simple doesn't mean you don't want the component. For example, `Array.forEach` could very well just be a standard for loop. But `Array.forEach` *looks* nicer and allows the user to not worry about the implementation of for loops to use it. – Dan Apr 30 '15 at 09:33
  • @DanPantry So you mean we use promises just to avoid multiple callback stacks. Its good for memory optimization that's it. – qnimate Apr 30 '15 at 09:34
  • 2
    Promises allow you to write `Promise(x).then(y).then(z)` instead of `x(function(){y(function(){z()})})` that's all. It doesn't change how javascript fundamentally work – slebetman Apr 30 '15 at 09:35
  • It does not optimize memory at all. All of the same code is still invoked. It's just an abstraction, similar to how ES6 classes are abstractions around the javascript prototype chain rather than being actual classes. Promises make asynchronous code fundamentally easier to read. – Dan Apr 30 '15 at 09:35
  • 1
    It just changes the way your code look. Nothing more. But keep in mind that code that's easy to reason about are also easier to debug so how a code looks is not unimportant – slebetman Apr 30 '15 at 09:36
  • 1
    The benefits of promises really come apparent when you want to compose asynchronous operations. It is difficult to wait on a set of asynchronous callbacks without the use of a helper library, such as `async`, for example. Promises also enable you to write asynchronous code in *nearly* the same way as synchronous code (and with `yield` in ES6 we'll get even closer to that). – Dan Apr 30 '15 at 09:39
  • @DanPantry After struggling for 2 days I finally found a meaningful explanation of Promises in ES6. Thanks everyone. – qnimate Apr 30 '15 at 09:40
  • Possible duplicate of [When is the body of a Promise executed?](https://stackoverflow.com/questions/42118900/when-is-the-body-of-a-promise-executed) – OfirD Jun 08 '21 at 16:55
  • @OfirD Rather, the other way round – Bergi Aug 26 '21 at 12:37

2 Answers2

17

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.

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
Dan
  • 10,282
  • 2
  • 37
  • 64
  • The mozilla version is easier to read though, as the official ones are in PDF – slebetman Apr 30 '15 at 09:01
  • I'll stick it in just in case, there might be some discrepancies especially to future readers (though the spec is on the final draft so it is unlikely) – Dan Apr 30 '15 at 09:02
  • 1
    In the future, this answer will be outdated and irrelevant (I should know, I have lots of js answers go that way from way back) :D – slebetman Apr 30 '15 at 09:07
  • Here's to hoping it's not.. :P I've put the final draft spec in, there was one discrepancy but it was fairly minor. – Dan Apr 30 '15 at 09:08
  • There is no official spec since it's a draft. Trust me this won't change. – Benjamin Gruenbaum Apr 30 '15 at 09:54
  • thanks for clarification, will update - this is probably the first time I've referred to a spec for behaviour. – Dan Apr 30 '15 at 09:55
  • Thank you @BenjaminGruenbaum for updating with the finalized specification :) – Dan Jun 26 '15 at 09:44
  • Sure thing, glad I could help – Benjamin Gruenbaum Jun 26 '15 at 09:50
  • I'm not seeing the answer in the spec. "Let completion be Call(..." is not a call of the executor but a declaration of the meaning of "completion." I can't seem to find an official answer about when the executor function (as in `new Promise(executor)`) is called. Is it always called immediately upon promise creation? Are there circumstances where it is only called upon calling `then()`? – Joe Lapp Feb 26 '16 at 14:19
  • @JoeLapp As per the answer below this, the `executor` will always be executed synchronously, but the callback provided to `then` will be executed asynchronously – Dan Feb 26 '16 at 14:31
  • @DanPantry In order for code within multiple promises to run asynchronously, the executor function of each promise would have to be called in succession synchronously, so that there's no waiting between the initiation of successive promises. Where is the official statement about when the executor function gets called? Is the executor function required to be called (synchronously) with the creation of a promise? I suspect that a clear answer won't use the words "synchronous" or "asynchronous" and will just tell exactly where the executor is called. – Joe Lapp Feb 26 '16 at 18:49
  • I just checked the RSVP library. It calls the executor immediately with the creation of a new Promise. I'm just wondering whether that's required (by some specification), or whether that can be deferred. The constructor calls `initializePromise()`, found here: https://github.com/tildeio/rsvp.js/blob/master/lib/rsvp/-internal.js – Joe Lapp Feb 26 '16 at 18:56
9

The other answer proves this, but let me talk about the reasoning:

Promise constructor

The promise constructor callback (as specified either in the ES6 spec or the constructor spec libraries implement) will always be executed synchronously - this is in order to extract a deferred (older form of promise construction) out of it in case you need to have access to the resolve callback:

var r;
var p new Promise(function(resolve, reject){
    r = resolve;
});
// use r here, for example
arr.push(r);

then callbacks

then will always be executed asynchronously, virtually all mainstream promise implementations (Native, bluebird, $q, Q, when, rsvp, promise, jQuery (as of 3.0) etc) as well as native promises implement (or implement a superset of, with more constraints) Promises/A+.

This is exactly the reason that Promises/A+ was created out of Promises/A. Asynchronous guarantees will be preserved and Zalgo won't be released. (Also see this post).

The fact this happens (asynchronous guarantee) is completely intentional and actively prevents race conditions. Code in- and outside of then will always execute in the same order.

Here is the relevant quote:

onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504