I think one principle I take so far is:
A promise is a thenable object, and so it takes the message then, or in other words, some code can invoke the then method on this object, which is part of the interface, with a fulfillment handler, which is "the next step to take", and a rejection handler, which is "the next step to take if it didn't work out." It is usually good to return a new promise in the fulfillment handler, so that other code can "chain" on it, which is saying, "I will also tell you the next step of action, and the next step of action if you fail, so call one of them when you are done."
However, on a JavaScript.info Promise blog page, it says the fulfillment handler can return any "thenable" object (that means a promise-like object), but this thenable object's interface is
.then(resolve, reject)
which is different from the usual code, because if a fulfillment handler returns a new promise, this thenable object has the interface
.then(fulfillmentHandler, rejectionHandler)
So the code on that page actually gets a resolve and call resolve(someValue). If fulfillmentHandler is not just another name for resolve, then why is this thenable different?
The thenable code on that page:
class Thenable {
constructor(num) {
this.num = num;
}
then(resolve, reject) {
alert(resolve); // function() { native code }
// resolve with this.num*2 after the 1 second
setTimeout(() => resolve(this.num * 2), 1000); // (**)
}
}
new Promise(resolve => resolve(1))
.then(result => {
return new Thenable(result); // (*)
})
.then(alert); // shows 2 after 1000ms
A thenable is any object containing a method whose identifier is then.
What follows is the simplest thenable one could write. When given to a Promise.resolve call, a thenable object is coerced into a pending Promise object:
const thenable = {
then() {}, // then does nothing, returns undefined
};
const p = Promise.resolve(thenable);
console.log(p); // Promise { <pending> }
p.then((value) => {
console.log(value); // will never run
}).catch((reason) => {
console.log(reason); // will never run
});
The point of writing a thenable is for it to get coerced into a promise at some point in our code. But a promise that never settles isn't useful. The example above has a similar outcome to:
const p = new Promise(() => {}); //executor does nothing, returns undefined
console.log({ p }); // Promise { <pending> }
p.then((value) => {
console.log(value); // will never run
}).catch((reason) => {
console.log(reason); // will never run
});
When coercing it to a promise, JavaScript treats the thenable's then method as the executor in a Promise constructor (though from my testing in Node it appears that JS pushes a new task on the stack for an executor, while for a thenable's then it enqueues a microtask).
A thenable's then method is NOT to be seen as equivalent to promise's then method, which is Promise.prototype.then.
Promise.prototype.then is a built-in method. Therefore it's already implemented and we just call it, passing one or two callback functions as parameters:
// Let's write some callback functions...
const onFulfilled = (value) => {
// our code
};
const onRejected = (reason) => {
// our code
};
Promise.resolve(5).then(onFulfilled, onRejected); // ... and pass both to a call to then
The executor callback parameter of a Promise constructor, on the other hand, is not built-in. We must implement it ourselves:
// Let's write an executor function...
const executor = (resolve, reject) => {
// our asynchronous code with calls to callbacks resolve and/or reject
};
const p = new Promise(executor); // ... and pass it to a Promise constructor
/*
The constructor will create a new pending promise,
call our executor passing the new promise's built-in resolve & reject functions
as first and second parameters, then return the promise.
Whenever the code inside our executor runs (asynchronously if we'd like), it'll have
said promise's resolve & reject methods at its disposal,
in order to communicate that they must respectivelly resolve or reject.
*/
A useful thenable
Now for a thenable that actually does something. In this example, Promise.resolve coerces the thenable into a promise:
const usefulThenable = {
// then method written just like an executor, which will run when the thenable is
// coerced into a promise
then(resolve, reject) {
setTimeout(() => {
const grade = Math.floor(Math.random() * 11)
resolve(`You got a ${grade}`)
}, 1000)
},
}
// Promise.resolve coerces the thenable into a promise
let p = Promise.resolve(usefulThenable)
// DO NOT CONFUSE the then() call below with the thenable's then.
// We NEVER call a thenable's then. Also p is not a thenable, anyway. It's a promise.
p.then(() => {
console.log(p) // Promise { 'You got a 9' }
})
console.log(p) // Promise { <pending> }
Likewise, the await operator also coerces a thenable into a promise
console.log('global code has control')
const usefulThenable = {
// then() will be enqueued as a microtask when the thenable is coerced
// into a promise
then(resolve, reject) {
console.log("MICROTASK: usefulThenable's then has control")
setTimeout(() => {
console.log('TASK: timeout handler has control')
const grade = Math.floor(Math.random() * 11)
resolve(`You got a ${grade}`)
}, 1000)
},
}
// Immediately Invoked Function Expression
let p = (async () => {
console.log('async function has control')
const result = await usefulThenable //coerces the thenable into a promise
console.log('async function has control again')
console.log(`async function returning '${result}' implicitly wrapped in a Promise.resolve() call`)
return result
})()
console.log('global code has control again')
console.log({ p }) // Promise { <pending> }
p.then(() => {
console.log('MICROTASK:', { p }) // Promise { 'You got a 10' }
})
console.log('global code completed execution')
The output:
/*
global code has control
async function has control
global code has control again
{ p: Promise { <pending> } }
global code completed execution
MICROTASK: usefulThenable's then has control
TASK: timeout handler has control
async function has control again
async function returning 'You got a 10' implicitly wrapped in a Promise.resolve() call
MICROTASK: { p: Promise { 'You got a 10' } }
*/
TL;DR: Always write the thenable's then method as you would the executor parameter of a Promise constructor.
In
let p2 = p1.then( onfulfilled, onrejected)
where p1 is a Promise object, the then call on p1 returns a promise p2 and records 4 values in lists held internally by p1:
the value of onfulfilled,
the value of the resolve function passed to the executor when creating p2 - let's call it resolve2,
the value of onrejected,
the value of the reject function passed to the executor when creating p2 - let's call it reject2.
1. and 3. have default values such that if omitted they pass fulfilled values of p1 on to p2, or reject p2 with the rejection reason of p1 respectively.
2. or 4. (the resolve and reject functions for p2) are held internally and are not accessible from user JavaScript.
Now let's assume that p1 is (or has been) fullfilled by calling the resolve function passed to its executor with a non thenable value. Native code will now search for existing onfulfilled handlers of p1, or process new ones added:
Each onfulfilled handler (1 above) is executed from native code inside a try/catch block and its return value monitored. If the return value, call it v1, is non-thenable, resolve for p2 is called with v1 as argument and processing continues down the chain.
If the onfulfilled handler throws, p2 is rejected (by calling 4 above) with the error value thrown.
If the onfulfilled handler returns a thenable (promise or promise like object), let's call it pObject, pObject needs to be set up pass on its settled state and value to p2 above.
This is achieved by calling
pObject.then( resolve2, reject2)
so if pObject fulfills, its non-thenable success value is used to resolve p2, and if it rejects its rejection value is used to reject p2.
The blog post defines its thenable's then method using parameter names based on how it is being used in the blog example (to resolve or reject a promise previously returned by a call to then on a native promise). Native promise resolve and reject functions are written in native code, which explains the first alert message.
After writing down the whole explanation, the short answer is: it is because the JS promise system passed in a resolve and reject as the fulfillmentHandler and rejectionHandler. The desired fulfillmentHandler in this case is a resolve.
When we have the code
new Promise(function(resolve, reject) {
// ...
}).then(function() {
return new Promise(function(resolve, reject) {
// ...
});
}).then(function() { ...
We can write the same logic, using
let p1 = new Promise(function(resolve, reject) {
// ...
});
let p2 = p1.then(function() {
let pLittle = new Promise(function(resolve, reject) {
// ...
resolve(vLittle);
});
return pLittle;
});
The act of returning pLittle means: I am returning a promise, a thenable, pLittle. Now as soon as the receiver gets this pLittle, make sure that when I resolve pLittle with value vLittle, your goal is to immediately resolve p2 also with vLittle, so the chainable action can go on.
How does it do it?
It probably has some code like:
pLittle.then(function(vLittle) { // ** Our goal **
// somehow the code can get p2Resolve
p2Resolve(vLittle);
});
The code above says: when pLittle is resolved with vLittle, the next action is to resolve p2 with the same value.
So somehow the system can get p2Resolve, but inside the system or "blackbox", the function above
function(vLittle) {
// somehow the code can get p2Resolve
p2Resolve(vLittle);
}
is probably p2Resolve (this is mainly a guess, as it explains why everything works). So the system does
pLittle.then(p2Resolve);
Remember that
pLittle.then(fn)
means
passing the resolved value of pLittle to fn and invoke fn, so
pLittle.then(p2Resolve);
is the same as
pLittle.then(function(vLittle) {
p2Resolve(vLittle)
});
which is exactly the same as ** Our goal** above.
What it means is, the system passes in a "resolve", as a fulfillment handler. So at this exact moment, the fulfillment handler and the resolve is the same thing.
Note that in the Thenable code in the original question, it does
return new Thenable(result);
this Thenable is not a promise, and you can't resolve it, but since it is not a promise object, that means that promise (like p2) is immediately resolved as a rule of what is being returned, and that's why the then(p2Resolve) is immediately called.
So I think this count on the fact that, the internal implementation of ES6 Promise passes the p2Resolve into then() as the first argument, and that's why we can implement any thenable that takes the first argument resolve and just invoke resolve(v).
I think the ES6 specification a lot of time writes out the exact implementation, so we may somehow work with that. If any JavaScript engine works slightly differently, then the results can change. I think in the old days, we were told that we are not supposed to know what happens inside the blackbox and should not count on how it work -- we should only know the interface. So it is still best not to return a thenable that has the interface then(resolve, reject), but to return a new and authentic promise object that uses the interface then(fulfillmentHandler, rejectionHandler).
Related
Suppose I have the following Promise:
function doSomethingAsynchronous() {
return new Promise((resolve) => {
const result = doSomeWork();
setTimeout(() => {
resolve(result);
}), 100);
});
}
At which point in time is doSomeWork() called? Is it immediately after or as the Promise is constructed? If not, is there something additional I need to do explicitly to make sure the body of the Promise is run?
Immediately, yes, by specification.
From the MDN:
The executor function is executed immediately by the Promise implementation, passing resolve and reject functions (the executor is called before the Promise constructor even returns the created object)
This is defined in the ECMAScript specification (of course, it's harder to read...) here (Step 9 as of this edit, showing that the executor is called synchronously):
Let completion be Completion(Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »)).
(my emphasis)
This guarantee may be important, for example when you're preparing several promises you then pass to all or race, or when your executors have synchronous side effects.
You can see from below the body is executed immediately just by putting synchronous code in the body rather than asynchronous:
function doSomethingAsynchronous() {
return new Promise((resolve) => {
console.log("a");
resolve("promise result");
});
}
doSomethingAsynchronous();
console.log("b");
The result shows the promise body is executed immediately (before 'b' is printed).
The result of the Promise is retained, to be released to a 'then' call for example:
function doSomethingAsynchronous() {
return new Promise((resolve) => {
console.log("a");
resolve("promise result");
});
}
doSomethingAsynchronous().then(function(pr) {
console.log("c:" + pr);
});
console.log("b");
Result:
a
b
c:promise result
Same deal with asynchronous code in the body except the indeterminate delay before the promise is fulfilled and 'then' can be called (point c). So a and b would be printed as soon as doSomethingAsynchronous() returns but c appears only when the promise is fulfilled ('resolve' is called).
What looks odd on the surface once the call to then is added, is that b is printed before c even when everything is synchronous.
Surely a would print, then c and finally b?
The reason why a, b and c are printed in that order is because no matter whether code in the body is async or sync, the then method is always called asynchronously by the Promise.
In my mind, I imagine the then method being invoked by something like setTimeout(()=>{then(pr)},0) in the Promise once resolve is called. I.e. the current execution path must complete before the function passed to then will be executed.
Not obvious from the Promise specification why it does this?
My guess is it ensures consistent behavior regarding when then is called (always after current execution thread finishes) which is presumably to allow multiple Promises to be stacked/chained together before kicking off all the then calls in succession.
Yes, when you construct a Promise the first parameter gets executed immediately.
In general, you wouldn't really use a promise in the way you did, as with your current implementation, it would still be synchronous.
You would rather implement it with a timeout, or call the resolve function as part of an ajax callback
function doSomethingAsynchronous() {
return new Promise((resolve) => {
setTimeout(function() {
const result = doSomeWork();
resolve(result);
}, 0);
});
}
The setTimeout method would then call the function at the next possible moment the event queue is free
From the EcmaScript specification
The executor function is executed immediately by the Promise
implementation, passing resolve and reject functions (the executor is
called before the Promise constructor even returns the created object)
Consider the following code:
let asyncTaskCompleted = true
const executorFunction = (resolve, reject) => {
console.log("This line will be printed as soon as we declare the promise");
if (asyncTaskCompleted) {
resolve("Pass resolved Value here");
} else {
reject("Pass reject reason here");
}
}
const myPromise = new Promise(executorFunction)
When we execute the above code, executorFunction will be called automatically as soon as we declare the Promise, without us having to explicitly invoke it.
Suppose I have the following Promise:
function doSomethingAsynchronous() {
return new Promise((resolve) => {
const result = doSomeWork();
setTimeout(() => {
resolve(result);
}), 100);
});
}
At which point in time is doSomeWork() called? Is it immediately after or as the Promise is constructed? If not, is there something additional I need to do explicitly to make sure the body of the Promise is run?
Immediately, yes, by specification.
From the MDN:
The executor function is executed immediately by the Promise implementation, passing resolve and reject functions (the executor is called before the Promise constructor even returns the created object)
This is defined in the ECMAScript specification (of course, it's harder to read...) here (Step 9 as of this edit, showing that the executor is called synchronously):
Let completion be Completion(Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »)).
(my emphasis)
This guarantee may be important, for example when you're preparing several promises you then pass to all or race, or when your executors have synchronous side effects.
You can see from below the body is executed immediately just by putting synchronous code in the body rather than asynchronous:
function doSomethingAsynchronous() {
return new Promise((resolve) => {
console.log("a");
resolve("promise result");
});
}
doSomethingAsynchronous();
console.log("b");
The result shows the promise body is executed immediately (before 'b' is printed).
The result of the Promise is retained, to be released to a 'then' call for example:
function doSomethingAsynchronous() {
return new Promise((resolve) => {
console.log("a");
resolve("promise result");
});
}
doSomethingAsynchronous().then(function(pr) {
console.log("c:" + pr);
});
console.log("b");
Result:
a
b
c:promise result
Same deal with asynchronous code in the body except the indeterminate delay before the promise is fulfilled and 'then' can be called (point c). So a and b would be printed as soon as doSomethingAsynchronous() returns but c appears only when the promise is fulfilled ('resolve' is called).
What looks odd on the surface once the call to then is added, is that b is printed before c even when everything is synchronous.
Surely a would print, then c and finally b?
The reason why a, b and c are printed in that order is because no matter whether code in the body is async or sync, the then method is always called asynchronously by the Promise.
In my mind, I imagine the then method being invoked by something like setTimeout(()=>{then(pr)},0) in the Promise once resolve is called. I.e. the current execution path must complete before the function passed to then will be executed.
Not obvious from the Promise specification why it does this?
My guess is it ensures consistent behavior regarding when then is called (always after current execution thread finishes) which is presumably to allow multiple Promises to be stacked/chained together before kicking off all the then calls in succession.
Yes, when you construct a Promise the first parameter gets executed immediately.
In general, you wouldn't really use a promise in the way you did, as with your current implementation, it would still be synchronous.
You would rather implement it with a timeout, or call the resolve function as part of an ajax callback
function doSomethingAsynchronous() {
return new Promise((resolve) => {
setTimeout(function() {
const result = doSomeWork();
resolve(result);
}, 0);
});
}
The setTimeout method would then call the function at the next possible moment the event queue is free
From the EcmaScript specification
The executor function is executed immediately by the Promise
implementation, passing resolve and reject functions (the executor is
called before the Promise constructor even returns the created object)
Consider the following code:
let asyncTaskCompleted = true
const executorFunction = (resolve, reject) => {
console.log("This line will be printed as soon as we declare the promise");
if (asyncTaskCompleted) {
resolve("Pass resolved Value here");
} else {
reject("Pass reject reason here");
}
}
const myPromise = new Promise(executorFunction)
When we execute the above code, executorFunction will be called automatically as soon as we declare the Promise, without us having to explicitly invoke it.
I have the following code:
function myPromiseFunc() {
return new Promise((resolve) => {
resolve(Promise.resolve(123));
});
}
As we know Promise.resolve method resolves Promise with a plain value immediately.
So Promise.resolve(123) -> Promise<fulfilled>
But:
console.log(myPromiseFunc());
will return Promise with status pending. Why? Is resolve function passed to executor async? Cause this:
setTimeout(console.log, 0, res);
will return Promise<fulfilled>.
I know Promises use microtasks but it's supposed to use only for handlers.
Promises/A+ says:
[[Resolve]](promise, x) -> If/when x is a promise and fulfilled, fulfill promise with the same value.
By the way. This snipped will return Promise<fulfilled>:
function myPromiseFunc() {
return new Promise((resolve) => {
resolve(123);
});
}
So it looks like resolve is async only when Promise passed as a value.
Please, help to understand. Thank you!
According to the specification of the resolve function passed to the executor in new Promise((resolve, reject) => ...):
When a promise resolve function is called with argument resolution, the following steps are taken:
Let F be the active function object.
Assert: F has a [[Promise]] internal slot whose value is an Object.
Let promise be F.[[Promise]].
Let alreadyResolved be F.[[AlreadyResolved]].
If alreadyResolved.[[Value]] is true, return undefined.
Set alreadyResolved.[[Value]] to true.
If SameValue(resolution, promise) is true, then
Let selfResolutionError be a newly created TypeError object.
Return RejectPromise(promise, selfResolutionError).
If Type(resolution) is not Object, then
Return FulfillPromise(promise, resolution).
Let then be Get(resolution, "then").
If then is an abrupt completion, then
Return RejectPromise(promise, then.[[Value]]).
Let thenAction be then.[[Value]].
If IsCallable(thenAction) is false, then
Return FulfillPromise(promise, resolution).
Let thenJobCallback be HostMakeJobCallback(thenAction).
Let job be NewPromiseResolveThenableJob(promise, resolution, thenJobCallback).
Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
Return undefined.
Lots of technical jargon, but the most important bits for your question is that resolution is the value you passed to it. If it's (roughly) a non-Promise, you'll end up either in 8.1 (for non-objects) or 12.1 (for non-callable objects), which will all immediately fulfill the promise. If you passed a value with a then function (e.g. a Promise), it'll do all the steps starting from 13 where it basically queues up the .then and follows the whole "my fulfillment depends on another Promise's fulfillment".
I think the short version of the question is: if the fulfillment handler supplied to a .then() returns a new promise, how does this new promise "unwrap" to the promise returned by .then()? (unwrap seems like a terminology used in the promise technology). (and if it is not returning a new promise but just a "thenable", how does it unwrap?)
To chain several time-consuming asynchronous promises together, such as doing several network fetches described on this page or a series of animations, I think the standard method is stated as:
In the fulfillment handler passed to .then(), create and return a new promise p, and in the executor passed to the constructor that created p, do the time-consuming task, and resolve this new promise p when done.
Sometimes I may take this as: this is just the way it is (how to chain time-consuming promises), or it is the language feature, and you can just considered it to be happening by magic (if it is a language feature).
But is it true that this is done by standard procedure how it would be handled if the fulfillment handler returns a new promise?
I wrote out some rough draft of how it could be done below. And to state it in a few sentences, it is
p1 is the first promise.
then() returns a new promise p2
the then() remembers what p2 is, as an internal property of p1.
when the fulfillment handler passed to then eventually get invoked (when p1 is resolved), the returned value is examined. If it is an object that has a property named then and is a function, then this object is treated as a thenable, which means it can be a genuine promise or just a dummy thenable (let's call it p1_New).
Immediately, this is invoked: p1_New.then(() => { resolveP2() })
Let's say p1_New is a genuine promise. When we resolve p1_New, it will resolve p2, and p2 will perform its "then" fulfillment handler, and the series of time-consuming promises can go on.
So this is the rough draft of code:
let p1 = new Promise(function(resolve, reject) {
// does something that took 10 seconds
resolve(someValue);
});
After the above code, the state of p1 is:
p1 = {
__internal__resolved_value: undefined,
__internal__state: "PENDING",
__internal__executor: function() {
// this is the executor passed to the constructor Promise().
// Something is running and the resolve(v) statement probably
// will do
// this.__internal__onResolve(v)
},
__internal__onResolve: function(resolvedValue) {
if (this.__internal__then_fulfillHandler) { // if it exists
let vFulfill = this.__internal__then_fulfillHandler(this.__internal__resolved_value);
// if the fulfillment handler returns a promise (a thenable),
// then immediately set this promise.then(fn1)
// where fn1 is to call this.__internal__resolveNewPromise()
// so as to resolve the promise newPromise that I am returning
if (vFulfill && typeof vFulfill.then === "function") { // a promise, or thenable
vFulfill.then(function() {
this.__internal__resolveNewPromise(this.__internal__resolved_value)
})
}
}
},
// in reality, the then should maintain an array of fulfillmentHandler
// because `then` can be called multiple times
then: function(fulfillmentHandler, rejectionHandler) {
this.__internal__then_fulfillHandler = fulfillmentHandler;
// create a new promise newPromise to return in any case
let newPromise = new Promise(function(resolve, reject) {
this.__internal__resolveNewPromise = resolve;
});
// if promise already resolved, then call the onResolve
// to do what is supposed to be done whenever this promise resolves
if (this.__internal__state === "RESOLVED") {
this.__internal__onResolve(this.__internal__resolved_value);
}
return newPromise;
}
}
and that's how when p1_New is resolved, then p2 is resolved, and the fulfillment handler passed to p2.then() will go on.
let p2 = p1.then(function() {
p1_New = new Promise(function(resolve, reject) {
// does something that took 20 seconds
resolve(someValue);
});
return p1_New;
});
p2.then(fulfillmentHandler, rejectionHandler);
But what if p1_New is not really a promise, but just a dummy thenable written this way:
class Thenable {
constructor(num) {
this.num = num;
}
then(resolve, reject) {
alert(resolve); // function() { native code }
// resolve with this.num*2 after the 1 second
setTimeout(() => resolve(this.num * 2), 1000); // (**)
}
}
new Promise(resolve => resolve(1))
.then(result => {
return new Thenable(result); // (*)
})
.then(alert); // shows 2 after 1000ms
it is doing the time-consuming task in its then(). That's fine too: if we look at step 5 above:
Immediately, this is invoked: p1_New.then(() => { resolveP2() })
that is, then is invoked immediately, and with the first function passed in being able to resolve p2. So that thenable performs some time-consuming task, and when done, call its first parameter (the function that resolves p2), and the whole sequence can go on like before.
But in this case, it is the then() doing the time-consuming task, not the executor doing it (the executor passed to the constructor of a new promise). In a way, it is like the then() has become the executor.
I understand promises more now, it seems this is the standard way.
Whatever the fulfillment handler returns: a primitive value, an object that is not a thenable, a thenable that is like a fake promise, or a real promise, will all be set up so that it will resolve p, where p is the promise that is returned by then.
This is the standard procedure, and what is called unwrapping. We can wrap and wrap a promise by many layers, when it unwraps from the inside, it will unwrap until it cannot continue.
And it is true, when an thenable fake promise, or a real promise is returned, it will trigger this action to happen underneath:
obj.then(resolveP) where resolveP is the resolve function that can resolve p.
That how that promise resolves p, and how the thenable can do asynchronous task and use the first argument passed to it as a resolve function to "resolve" p.
Suppose I have the following Promise:
function doSomethingAsynchronous() {
return new Promise((resolve) => {
const result = doSomeWork();
setTimeout(() => {
resolve(result);
}), 100);
});
}
At which point in time is doSomeWork() called? Is it immediately after or as the Promise is constructed? If not, is there something additional I need to do explicitly to make sure the body of the Promise is run?
Immediately, yes, by specification.
From the MDN:
The executor function is executed immediately by the Promise implementation, passing resolve and reject functions (the executor is called before the Promise constructor even returns the created object)
This is defined in the ECMAScript specification (of course, it's harder to read...) here (Step 9 as of this edit, showing that the executor is called synchronously):
Let completion be Completion(Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »)).
(my emphasis)
This guarantee may be important, for example when you're preparing several promises you then pass to all or race, or when your executors have synchronous side effects.
You can see from below the body is executed immediately just by putting synchronous code in the body rather than asynchronous:
function doSomethingAsynchronous() {
return new Promise((resolve) => {
console.log("a");
resolve("promise result");
});
}
doSomethingAsynchronous();
console.log("b");
The result shows the promise body is executed immediately (before 'b' is printed).
The result of the Promise is retained, to be released to a 'then' call for example:
function doSomethingAsynchronous() {
return new Promise((resolve) => {
console.log("a");
resolve("promise result");
});
}
doSomethingAsynchronous().then(function(pr) {
console.log("c:" + pr);
});
console.log("b");
Result:
a
b
c:promise result
Same deal with asynchronous code in the body except the indeterminate delay before the promise is fulfilled and 'then' can be called (point c). So a and b would be printed as soon as doSomethingAsynchronous() returns but c appears only when the promise is fulfilled ('resolve' is called).
What looks odd on the surface once the call to then is added, is that b is printed before c even when everything is synchronous.
Surely a would print, then c and finally b?
The reason why a, b and c are printed in that order is because no matter whether code in the body is async or sync, the then method is always called asynchronously by the Promise.
In my mind, I imagine the then method being invoked by something like setTimeout(()=>{then(pr)},0) in the Promise once resolve is called. I.e. the current execution path must complete before the function passed to then will be executed.
Not obvious from the Promise specification why it does this?
My guess is it ensures consistent behavior regarding when then is called (always after current execution thread finishes) which is presumably to allow multiple Promises to be stacked/chained together before kicking off all the then calls in succession.
Yes, when you construct a Promise the first parameter gets executed immediately.
In general, you wouldn't really use a promise in the way you did, as with your current implementation, it would still be synchronous.
You would rather implement it with a timeout, or call the resolve function as part of an ajax callback
function doSomethingAsynchronous() {
return new Promise((resolve) => {
setTimeout(function() {
const result = doSomeWork();
resolve(result);
}, 0);
});
}
The setTimeout method would then call the function at the next possible moment the event queue is free
From the EcmaScript specification
The executor function is executed immediately by the Promise
implementation, passing resolve and reject functions (the executor is
called before the Promise constructor even returns the created object)
Consider the following code:
let asyncTaskCompleted = true
const executorFunction = (resolve, reject) => {
console.log("This line will be printed as soon as we declare the promise");
if (asyncTaskCompleted) {
resolve("Pass resolved Value here");
} else {
reject("Pass reject reason here");
}
}
const myPromise = new Promise(executorFunction)
When we execute the above code, executorFunction will be called automatically as soon as we declare the Promise, without us having to explicitly invoke it.