Preface
Pay attention to the translation of corresponding words, which helps understand the entire pattern
-
promise: promise
-
pending: pending
-
settled: settled
-
resolve(d): resolved
-
reject(ed): rejected
-
onFulfilled: fulfilled (event)
-
onRejected: rejected (event)
-
then: then, next
-
catch: catch
-
race: race
-
future: future (value)
-
thenable: thenable (has then property)
I. What is Promise
A set of asynchronous operation handling mechanisms, Promise A+ is the specification, jQuery, ES2015, q's promise are all implementations of this specification
II. What Characteristics Does Promise Have
-
Easier to control the execution order of asynchronous operations
-
Can flexibly handle errors in a series of asynchronous operations
-
Supports chain calling, easy to write
-
Can eliminate callback pyramid, looks good
III. What is Promise Used For
-
Implement serial task queue
-
Implement serial task pipeline
-
Handle serial tasks where execution order can only be determined at runtime
(Due to space limitations, specific applicable scenarios will be expanded later)
IV. Promise Types
Internal Attributes [[PromiseStatus]]/[[PromiseValue]]
[[PromiseStatus]]: pending | resolved | rejected
The latter two are collectively called settled, pending is the initial state, if after new Promise(func), neither resolve nor reject in func has been executed yet, at this time status=pending
[[PromiseValue]]: undefined | resolve/reject's parameter | Promise.resolve/reject's !thenable parameter | onFulfilled/onRejected's return value
When then is called, this value will be passed to then's onFulfilled or onRejected, if resolve and reject are never executed, then then's onFulfilled and onRejected will also never be executed
Constructor Parameters resolve/reject
resolve
After executing resolve, triggers then's onFulfilled callback, and passes resolve's parameter to onFulfilled
reject
After executing reject, triggers then | catch's onRejected callback, and passes reject's parameter to onRejected
Note: If the following then does not declare an onRejected callback, it passes to the next then | catch's onRejected, still not there, continue passing... If it reaches the end without one, an error Uncaught (in promise) Error:xxx is thrown
Special: If resolve's parameter is a Promise object, then that object's final [[PromiseValue]] will be passed to the outer Promise object's subsequent then's onFulfilled/onRejected, for example:
// 外层 then 能够拿到内层 future 和 error
new Promise(function(resolve, reject) {
resolve(new Promise(function(resolve, reject) {
resolve(12);
// reject(new Error('reject in nested Promise'));
}).then(function(future) {
return future + 1;
}, function(error) {
return new Error('reject again in nested Promise');
}));
}).then(onFulfilled, onRejected);
// => 13 因为 onFulfilled 接收到了内层 promise 最终的 [[PromiseValue]]
// 如果是 reject,onFulfilled 将会接收到 Error('reject again')
V. Basic Syntax
1. Creating Promise Object
The basic syntax for creating a Promise is as follows:
// 创建
var promise = new Promise(function(resolve, reject) {
if (/*...*/) {
resolve(data);
}
else {
reject(new Error('error occurs'));
}
});
// 使用
// then
promise.then(function(future) {
// ...
}, function(error) {
// ...
});
// catch
promise.catch(function(error) {
// ...
});
Note: When new Promise(func), func will execute immediately (unlike generator which only declares but doesn't execute), during execution encountering resolve() pretends not to see it (P.S. Simply understand as pretending not to see it, explanation see comments), encountering reject() immediately throws an error (Note: Chrome46 will immediately throw an error, FF39 doesn't error, other polyfills, such as es6-promise、promise-polyfill don't error), comparison examples are as follows:
new Promise(function(resolve, reject) {
console.log('#1');
resolve(1);
console.log('#2');
});
// => #1 #2 不报错(遇到 resolve 装没看见)
// 其实内部属性 [[PromiseStatus]] 和 [[PromiseValue]] 都变了,但没有 then 提供的 onFulfilled 回调,看不到效果
new Promise(function(resolve, reject) {
console.log('#1');
reject(new Error('reject'));
console.log('#2');
});
// => #1 #2 Chrome46 报错:Uncaught (in promise) Error: reject(…)
// 因为后面没有 then/catch 提供的 onRejected 回调,异常没有被消费掉
P.S. reject's parameter can be of any type, but generally Error object is passed in to indicate the error reason
2. Promise.prototype.then(onFulfilled, onRejected) Then
Adds onFulfilled/onRejected callback handler to promise, when promise's resolve/reject executes, triggers corresponding handler
Note: When using promise.then to add callback functions, if promise is already in fulfilled or rejected state, then the corresponding method will be called immediately
Returns a Promise object (chain calling is possible), can be simply understood as:
-
If
onFulfilledis triggered, then the object's [[PromiseValue]]=onFulfilled's return value, [[PromiseStatus]]=resolvednew Promise(function(resolve, reject) { resolve(2); }).then(function(future) { return future * 2; }, onRejected).then(onFulfilled, onRejected); // onFulfilled 拿到了 4
-
If onRejected is triggered, then the object's [[PromiseValue]]=onRejected's return value, [[PromiseStatus]]=resolved
Special note: The returned object's state is resolved, not rejected
new Promise(function(resolve, reject) {
reject(2);
}).then(null, function(err) {
return err * 2;
}).then(onFulfilled, onRejected);
// 还是 onFulfilled 拿到了 4,而不是 onRejected
P.S. Actually [[PromiseValue]]=undefined, [[PromiseStatus]]=pending, but will still execute according to the above state
3. Promise.prototype.catch(onRejected) Catch
Equivalent to then(null, onRejected), returns a Promise object, can be simply understood as:
-
If onReject is triggered, then the returned object's [[PromiseValue]]=onReject's return value, [[PromiseStatus]]=resolved
-
If onReject is never triggered, then pretend not to see catch, directly return the original Promise object
var p = new Promise(function(resolve, reject) { resolve(2); }).catch(function() { console.log('onRejected'); return false; }); p.then(onFulfilled, onRejected); // onFulfilled 拿到了 2,catch 没有任何影响
P.S. Actually [[PromiseValue]]=undefined, [[PromiseStatus]]=pending, but will still execute according to the above state
4. Promise.all(iterable)
Returns a Promise object, when all promises in iterable are resolved, triggers onFulfilled, encounters the first negative promise and immediately exits, and triggers onRejected
//1.全肯定集合,触发 onFulfilled,传入的参数是集合中所有 promise 的 [[PromiseValue]] 组成的数组
Promise.all([getPromise(), getPromise()]).then(onFulfilled, onRejected);
//2.全否定集合,遇到第一个否定 promise 就退出,触发 onRejected,传入的参数是该 promise 的 [[PromiseValue]]
// Promise.all([getPromise(true), getPromise(true)]).then(onFulfilled, onRejected);
// 3.一般集合,同上
// Promise.all([getPromise(true), getPromise()]).then(onFulfilled, onRejected);
P.S. all, either all complete normally, or interrupt due to error, all stop
5. Promise.race(iterable) Race
Returns a Promise object, when any promise in iterable is resolved, immediately exits and triggers onFulfilled, encounters negative promise and immediately exits, and triggers onRejected
// 1.全肯定集合,遇到第一个肯定 promise 就退出,触发 onFulfilled,传入的参数是该 promise 的 [[PromiseValue]]
// Promise.race([getPromise(), getPromise()]).then(onFulfilled, onRejected);
// 2.全否定集合,遇到第一个否定 promise 就退出,触发 onRejected,传入的参数是该 promise 的 [[PromiseValue]]
// Promise.race([getPromise(true), getPromise(true)]).then(onFulfilled, onRejected);
// 3.一般集合,同上
// Promise.race([getPromise(true), getPromise()]).then(onFulfilled, onRejected);
P.S. "Race", regardless of whether the result is affirmative or negative, just want the fastest
6. Promise.resolve(value) Resolve
-
If
valueispromise, then passespromise'sresolve/reject's parameter to correspondingonFulfilled/onRejectedPromise.resolve(getPromise()).then(onFulfilled, onRejected); // onFulfilled 拿到 resolve 的参数 Promise.resolve(getPromise(true)).then(onFulfilled, onRejected); // onRejected 拿到 reject 的参数
-
If
valueis a thenable object (object withthenproperty), then wrapsobjinto a Promise object, this object'sthenisobj.thenPromise.resolve({ then: function(onFulfilled, onRejected) { // onFulfilled(1); onRejected(new Error('obj.then err'));
console.log('obj.then'); }}).then(onFulfilled, onRejected);
-
If
valueis a !thenable value, wraps that value into a Promise object, this object's [[PromiseValue]]=value, [[PromiseStatus]]=resolved, so when calling then method, onFulfilled will receive this valuePromise.resolve(1).then(onFulfilled, onRejected); // onFulfilled 拿到 1
7. Promise.reject(reason) Reject
Wraps reason into a Promise object, this object's [[PromiseValue]]=reason, [[PromiseStatus]]=rejected, so when calling then method, onRejected will receive this value
Note: There is no thenable/!thenable distinction here, treats all equally:
// 1.!thenable onRejected 将拿到 Error 对象
Promise.reject(new Error('static reject')).then(onFulfilled, onRejected);
// 2.promise onRejected 将拿到被包起来的肯定 Promise 对象
// 该对象的 [[PromiseValue]]=promise, [[PromiseStatus]]=rejected
Promise.reject(getPromise()).then(onFulfilled, onRejected);
// 3.thenable onRejected 将拿到{then: xxx}
Promise.reject({
then: function(onFulfilled, onRejected) {
console.log('obj.then');
}
}).then(onFulfilled, onRejected);
Complete test DEMO: promise instance
VI. Advanced Usage
-
Cooperate with generator (
generator) -
Cooperate with
async/await
(Due to space limitations, will discuss later)
No comments yet. Be the first to share your thoughts.