可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
As I plunge into studying Promise
s, my understanding has halted on the following question that I do not find discussed (all I find are specific discussions of the Promise
constructor, and the Promise
'then
' function - but not a discussion that compares their design patterns).
1. The Promise
constructor
From the MDN documentation, we have this use of the Promise constructor (with my comment added):
new Promise(function(resolve, reject) { ... }); // <-- Call this Stage 1
Function object with two arguments resolve
and reject
. The first
argument fulfills the promise, the second argument rejects it. We can
call these functions, once our operation is completed.
2. The then
function
Moving on to the then
function that can be called on a Promise
object (which returns a new Promise
object), we have the following function signature as described by the documentation (with my comments added):
:
p.then(onFulfilled, onRejected);
Chaining
Because the then
method returns a Promise, you can easily chain then
calls.
var p2 = new Promise(function(resolve, reject) {
resolve(1); // <-- Stage 1 again
});
p2.then(function(value) {
console.log(value); // 1
return value + 1; // <-- Call this Stage 2
}).then(function(value) {
console.log(value); // 2
});
My question
From the above code snippet, it seems clear to me that the value passed to the resolve
function in Stage 1 (in the second occurrence of resolve
- beneath (2), above) is passed on to the next stage (the first then
function that follows in the same code snippet). There is no return value at Stage 1. However, it is the return value at Stage 2 that is passed on to the next stage after that (the second then
function).
Is this lack of correspondence between the design pattern for the creation of a Promise
, and the use of the then
function on an existing promise (which also returns a Promise
), just a historical fluke (one requires calling a callback but returns nothing, and the other returns a value but does not call a callback)?
Or am I missing an underlying reason why the Promise
constructor utilizes a different design pattern than the then
function?
回答1:
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.
(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()
!
回答2:
There is no correspondence between the Promise
constructor and the then
method because they are two independent things, designed for different purposes.
The Promise
constructor is only used for promisifying1 asynchronous functions. Indeed, as you say, it is built on invoking resolve
/reject
callbacks to asynchronously send values, and there are no return values in that case.
That the Promise
constructor itself does take this "resolver" callback (to which it synchronously passes resolve
and reject
) is in fact an enhancement of the older deferred pattern, and bears no intended similarity to the then
callbacks.
var p = new Promise(function(res, rej) { | var def = Promise.Deferred();
setTimeout(res, 100); | setTimeout(def.resolve, 100);
}); | var p = def.promise;
The then
callbacks in contrast are classical asynchronous callbacks, with the additional feature that you can return
from them. They are being invoked asynchronously to receive values.
p.then(function(val) { … });
To sum up the differences:
Promise
is a constructor, while then
is a method
Promise
takes one callback, while then
takes up to two
Promise
invokes its callback synchronously, while then
invokes its callbacks asynchronously
Promise
always invokes its callback,
then
might not invoke its callbacks (if the promise is not fulfilled/rejected)
Promise
passes the capabilities to resolve/reject a promise to the callback,
then
passes the result value / rejection reason of the promise it was called on
Promise
invokes its callback for the purpose of executing side effects (call reject
/resolve
),
then
invokes its callbacks for their result values (for chaining)
Yes, both do return promises, though they share that trait with many other functions (Promise.resolve
, Promise.reject
, fetch
, …). In fact all of these are based on the same promise construction and resolve/reject capabilities that also the Promise
constructor provides, though that's not their primary purpose. then
basically offers the ability to attach onFulfilled
/onRejected
callbacks to an existing promise, which is rather diametral to the Promise
constructor.
That both utilise callbacks is just coincidential - not a historical fluke, but rather coadaption of a language feature.
1: Ideally, you would never need this because all natively asynchronous APIs return promises
回答3:
The whole point of the promise constructor executor function is to disseminate resolve and reject functions to non-promise-using code, to wrap it and convert it to use a promise. If you wanted to limit this to synchronous functions only, then yes, a return value from the function could have been used instead, but that would have been silly since the useful part is to disseminate the resolver and reject functions to code that actually runs later (way after the return), e.g. to callbacks passed in to some asynchronous API.
回答4:
Inspired by the previous answers (I'll address the part that was most confusing to me):
The resolve
and reject
arguments in the Promise constructor are not functions you define. Think of them as hooks that you get to embed into your async operation code (usually you resolve
with success response and reject
with failure reason) , so that javascript has a way to eventually mark the Promise as Fulfilled or Rejected depending on the outcome of your async operation; once that happens, the appropriate function you defined in then(fun1, fun2)
is triggered to consume the Promise (either fun1(success_response)
or fun2(failure_reason)
, depending on whether the Promise is Fulfilled/Rejected). Since fun1
and fun2
are plain old javascript functions (they just happen to take the future outcome of your async operation as arguments), they return
values (which can be undefined
if you don't explicitly return).
Also see great articles by Mozilla:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise