According to the Promise A+ spec,
2.2.2.1 If onFulfilled is a function, it must be called after promise is fulfilled, with promise’s value as its first argument.
However, what happens if a promise is passed in .then()?
Now we have this code:
var Promise = require('bluebird');
var func1 = function() {
return new Promise(function(resolve, reject) {
resolve('hello');
});
}
var wrapper = function() {
return func1();
}
var api = function() {
return wrapper()
.then(wrapper());
}
api().then(function(msg) {
console.log(msg);
});
Why is 'hello' get printed?
In the api function, wrapper() is evaluated as a promise through func1() and get passed in .then(). Right now, this promise acts 'onFulfilled' function according to the spec quoted. However, the spec says the onFulfilled(the returned promise in this case) is expecting a parameter passed in as the value of the previous promise calling .then(). But how can the value get passed into a promise?
I am not very good at explaining things. So if you are confused, can you explain why 'hello' is printed? How?
But how can the value get passed into a promise?
It can't and the part of the spec you quoted should have already told you that:
If onFulfilled is a function [...]
a promise is a not a function! (usually, and if it were, it would act like a function, not like a promise).
So calling .then basically doesn't do anything:
2.2.7.3 If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1.
promise1 is fulfilled with "hello", so that's what promise2 to is fulfilled with as well.
wrapper()/*promise 1*/.then(wrapper())/*promise 2*/.then(function(msg) {
console.log(msg);
});
is essentially
wrapper()/*promise 1*/.then(function(msg) {
console.log(msg);
});
and that's why "hello" is printed.
Now, if you passed wrapper, a function, instead, i.e. wrapper().then(wrapper).then(...), which is the same as
wrapper()/*a*/.then(function foo(result) {
return func1(); /*b*/
})/*c*/.then(function(msg) {
// ...
});
then foo would get the result from the first promise (a) (according to the part your quoted). And since the onFulfilled function returns a promise itself (b), the promise returned by .then (c) is resolved with value of that inner promise (b) (which happens to produce the same value as the first initial promise (a)).
This is described in section 2.3, where x is the value that is returned by onFulfilled.
If x is a promise, adopt its state:
If x is pending, promise must remain pending until x is fulfilled or rejected.
If/when x is fulfilled, fulfill promise with the same value.
If/when x is rejected, reject promise with the same reason.
Related
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.
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).
I've been trying to figure this one out and I'm wondering how the resolution value gets passed too getAnswer's then call. First I return the result of add which I think returns a Promise which allows me to use a then call on the then method on getAnswer, but then how does the second return statement get passed to it?
function add(num1, num2) {
return new Promise((resolve, reject) => {
setTimeout(() => {resolve(num1 + num2);}, 500)
});
}
function getAnswer() {
return add(5, 5).then((res) => {
console.log(res);
return new Promise((resolve, reject) => {
resolve("How does this get passed too getAnswer's then function?");
});
});
}
getAnswer().then((res) => {
console.log(res);
})
The Basics:
add returns a Promise -- which is an instance of the Promise object, every instance of Promise has a then method which you can use to observe that promise's resolution.
For chaining purposes, then has been designed in such a way that it returns a Promise itself (so, every then call in JavaScript will always return a new promise). The promise returned by then will be resolved with the return value of its handlers (more on this later).
Now, when you say:
return add(5, 5).then((res) => {
console.log(res);
return new Promise((resolve, reject) => {
resolve("How does this get passed too getAnswer's then function?");
});
});
You are not returning add's result, you are returning the promise that you created by calling then on add's result (this is just standard JS behavior).
Your Question:
but then how does the second return statement get passed to it?
Here is what the then method returns, according to MDN (the parts inside ([]) are my additions):
A Promise in the pending status ([this is what your first return statement is actually returning]). The handler function (onFulfilled or onRejected) gets then called asynchronously ([the handler in your example is the function you've passed to the then inside getAnswer]). After the invocation of the handler function, if the handler function:
returns a value, the promise returned by then gets resolved with the returned value as its value;
throws an error, the promise returned by then gets rejected with the thrown error as its value;
([this is your case -- your second return]) returns an already resolved promise, the promise returned by then gets resolved with that promise's value as its value;
returns an already rejected promise, the promise returned by then gets rejected with that promise's value as its value.
returns another pending promise object, the resolution/rejection of the promise returned by then will be subsequent to the resolution/rejection of the promise returned by the handler. Also, the value of the promise returned by then will be the same as the value of the promise returned by the handler.
Personally, whenever I see a Promise being returned by then's handlers, I just assume -- to simplify the situation for my mind -- that the Promise returned by then originally has been replaced with the Promise that was just returned by one of then's handlers. Of course, this mental mapping is parallel to the actual functionality, AFAIK.
So, to sum it up:
getAnswer returns a promise -- created by add(5, 5).then(...).
you then observe that returned promise using then (getAnswer().then(...)) -- unrelated here, but this call also creates a promise.
it happens that the handler for the observation of the promise returned by the add call (this handler is the function you pass to then in #1) returns a promise as well, and the rules are that if a handler of then returns a promise p, then whenever p is resolved with a value v, the original promise -- created by then -- will be resolved with v as well.
Finally, the handler you passed to the then in #2 to observe the promise returned by getAnswer() will be called with the value v from #3.
Feel free to ask for any clarifications, but before doing so, read this article thoroughly.
What is the difference between
resolve and onFulfilled in javascript promises ?
Similarly, what is the difference between
reject and onRejected ?
In simple words, I would just ask how does onsuccess callback of Promise.then(onsuccess, onreject) differs from Promise.resolve ()?
I am reading Javascript with Promises by Daniel Parker.
The book has mentioned both of them but I have not realized the difference between two yet.
While describing then in promises:
promise.then
promise.then([onFulfilled], [onRejected]) returns promise
The promise.then() method accepts an onFulfilled callback and an
onRejected
callback. People generally register onRejected callbacks using promise.catch()
instead of passing a second argument to then . The function then returns a promise that is resolved by the return value of the
onFulfilled or onRejected callback. Any error thrown inside the callback rejects the new promise with that error.
Also,
Promise.resolve
Promise.resolve([value|promise]) returns promise The Promise.resolve()
function is a convenience function for creating a promise that is
already resolved with a given value. If you pass a promise as the
argument to Promise.resolve(), the new promise is bound to the promise
you provided and it will be fulfilled or rejected accordingly.
Code:
function settled(promises) {
var alwaysFulfilled = promises.map(function (p) {
return p.then(
function onFulfilled(value) {
return { state: 'fulfilled', value: value };
},
function onRejected(reason) {
return { state: 'rejected', reason: reason };
}
);
});
return Promise.all(alwaysFulfilled);
}
};
simply put
when a promise is resolved any current or future onFullfilled functions will be called with the parameter to that function being the value of the resolve function
similarly with reject/onRejected
This question already has answers here:
How angular promise .then works
(2 answers)
Closed 7 years ago.
All:
I am pretty new to Promise, just curious how they get resolved, one thing confuse me is:
Some posts show using
var defer = $q.defer();
// some logic block
{
// if success doing something
defer.resolve();
}
return defer.promise;
But if use .then() function, promise is returned from .then(function(){}), I wonder how do I control if this promise resolved or not?
Another confuse is: If I use some chained .then() function, I wonder what is the relationship between them, are they same promise object which is just passed down or each .then will generate a new Promise object and return it?
As specified in this throughout and clear document:
QUESTION 1. I wonder how do I control if this promise resolved or not?
One of the Promise APIs support pecial functions that resolve() or reject() a Promise. So you may use the following functions in your code
var promise = new Promise(function(resolve, reject) {
// do a thing, possibly async, then…
if (/* everything turned out fine */) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
});
Rejections happen when a promise is explicitly rejected, but also implicitly
if an error is thrown in the constructor callback.
var jsonPromise = new Promise(function(resolve, reject) {
// JSON.parse throws an error if you feed it some
// invalid JSON, so this implicitly rejects:
resolve(JSON.parse("This ain't JSON"));
});
jsonPromise.then(function(data) {
// This never happens:
console.log("It worked!", data);
}).catch(function(err) {
// Instead, this happens:
console.log("It failed!", err);
});
In other variants the Promise is resolved with the return value that is passed to the next link in the chain.
QUESTION 2.
Promises are in some sense functions that will result in the future with some value. The result value is the return value from promise - so basically promise chaining ( .then(...).then... ) are chain of functions that wait till the previous one will end ( resolve with some value ). Then they are called with an argument which is the return value of the last executed function in the queue ( previous link in the chain ).
.then() returns a new promise object thus allowing chaining. (see remark for documentation link)
REMARK
There is great and small description of all Angular promises in official documentation under the section Promise API and next one - Chaining the promises.
This isn't an attempt to explain promises in their full glory - there are blogs for that. This is to answer your specific questions:
Q1:
But if I use .then() function, promise is returned from .then(function(){}), I wonder how do I control if this promise resolved or not?
The resolve handler function of .then controls how this promise is resolved:
If the handler function returns a non-promise value, then the promise with resolve with that value.
var thenPromise = originalPromise.then(function success() {
return "foo";
});
thenPromise.then(function(data){
console.log(data); // "foo"
});
If the handler function returns another promise, then the .then promise will resolve exactly how the new promise would resolve (or reject)
var thenPromise = originalPromise.then(function() {
return $timeout(function(){ return "foo"; }, 1000);
});
thenPromise.then(function(data){
console.log(data); // (after 1 second) "foo"
});
If the handler function throws an exception or if the the return is an explicitly rejected promise `$q.reject:
var thenPromise = originalPromise.then(function() {
return $q.reject("some error");
});
thenPromise.then(function(data){
console.log(data); // doesn't get here
})
.catch(function(err){
console.log(err); // "some error"
});
Q2:
If I use some chained .then() function, I wonder what is the relationship between them, are they same promise object which is just passed down or each .then will generate a new Promise object and return it?
Each .then generates its own promise.
var timeoutPromise = $timeout(afterTimeout, 1000);
var thenPromise = timeoutPromise.then(doSomething);
var anotherThenPromise = timeoutPromise.then(doSomethingElse);
If timeoutPromise resolves, then both doSomething and doSomethingElse would execute and depending on their outcome thenPromise and anotherThenPromise would have their respective resolutions.