Promise
同步與異步
一個主任務(wù)的執(zhí)行過程中調(diào)用了一個子任務(wù)
如果需要停下主任務(wù)的執(zhí)行,等待子任務(wù)執(zhí)行完畢,再繼續(xù)執(zhí)行主任務(wù).這種編程方式稱為同步編程.
如果不需要停下主任務(wù)的執(zhí)行,無論該子任務(wù)是否執(zhí)行完畢,都可以繼續(xù)執(zhí)行主任務(wù).這種編程方式成為異步編程.
一個簡單的例子
callback_async_simple.png
fnc執(zhí)行時,先調(diào)用了a,a會在1000ms后打印"callback",fnc無需等待1000ms后執(zhí)行b,而是a函數(shù)返回后,立即可以執(zhí)行b.
一個Promise的例子
promise_async_simple.png
回調(diào)與Promise
我們再來看一個異步的例子
callback_async_complex.png
如上,用回調(diào)的方式實(shí)現(xiàn)異步,很快導(dǎo)致了回調(diào)地獄.
Promise的出現(xiàn)就是為了替代回調(diào)的異步方案,讓異步編程以一種同步的語法出現(xiàn).
一個Promise實(shí)現(xiàn)異步編程的例子
promise_async.png
小結(jié)
Promise是JavaScript多種異步編程解決方案中的其中一種,回調(diào)也能實(shí)現(xiàn)異步編程,
Promise的出現(xiàn)就是為了替代掉回調(diào)的解決方案,讓異步編程以一種同步的語法呈現(xiàn).
Promise/A+的實(shí)現(xiàn)
Promise/A+ 只詳細(xì)規(guī)范了then方法,對于Promise的構(gòu)造方法和其他輔助或拓展方法沒有加以限制.
Promise的構(gòu)造方法
Promise構(gòu)造方法接受一個函數(shù)作為參數(shù),參數(shù)函數(shù)又接受兩個函數(shù)作為參數(shù).
promise_constructor.png
Promise有3種狀態(tài):待定(pending),已兌現(xiàn)(fulfilled),已拒絕(rejected).
promise_states.png
promise_states_property.png
then方法
Promise的then函數(shù)接受兩個參數(shù):
then_method_call.png
then方法的返回值必須為一個Promise.
then_method_return_promise.png
then方法中的參數(shù)有諸多限定
如果onFulfilled不是一個函數(shù),則其必須被忽略.
如果onRejected不是一個函數(shù),則其必須被忽略.
then_ignore_para.png
如果onFulfilled是一個函數(shù):
- 該函數(shù)在Promise變?yōu)橐褍冬F(xiàn)(fulfilled)狀態(tài)后必須調(diào)用,并且將Promise的終值作為該函數(shù)的第一個參數(shù).
- 該函數(shù)在Promise變?yōu)橐褍冬F(xiàn)(fulfilled)狀態(tài)之前不可調(diào)用.
- 該函數(shù)只能被調(diào)用一次.
如果onRejected是一個函數(shù):
- 該函數(shù)在Promise變?yōu)橐丫芙^(rejected)狀態(tài)后必須調(diào)用,并且將Promise的拒因作為該函數(shù)的第一個參數(shù).
- 該函數(shù)在Promise變?yōu)橐褍冬F(xiàn)(rejected)狀態(tài)之前不可調(diào)用.
- 該函數(shù)只能被調(diào)用一次.
then函數(shù)可以被同一個Promise調(diào)用多次
- 如果Promise已兌現(xiàn),所有onFulfilled依照被注冊的順序依次執(zhí)行
- 如果Promise已拒絕,所有onRejected依照被注冊的順序一次執(zhí)行
then_para_isfunciton_call.png
resolve_$_reject.png
onFulfilled 和 onRejected只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用.
onFulfilled 和 onRejected必須作為函數(shù)被調(diào)用(沒有this值)
settimeout_deal.png
settimeout_then.png
至此,我們實(shí)現(xiàn)了Promise的狀態(tài)變更以及then方法的參數(shù)的調(diào)用.讓我們回過頭再來處理then方法的返回值.
如果onFulfilled 或者 onRejected 返回值x,執(zhí)行下面的Promise解決過程,記為[[Resolve]](promise2, x)
resolve_promise.png
如果onFulfilled 或者 onRejected 拋出異常e,promise2必須以e為拒因拒絕.
ondeal_err.png
如果onFulfilled不是函數(shù)并且promise1已兌現(xiàn),promise2必須以promise1的終值兌現(xiàn).
如果onRejected不是函數(shù)并且promise1已拒絕,promise2必須以promise1的拒因拒絕.
此兩條在onFulfilled,onRejected的調(diào)用實(shí)現(xiàn)中已滿足.
then_ignore_para_red_box.png
接下來還剩下最后一個問題:Promise解決過程[[Resolve]](promise2, x)
如果promise和x引用同一個對象,使用"TypeError"作為拒因拒絕promise.
resolve_promise_refer_to_same.png
如果x是Promise,則promise接受x的狀態(tài).
如果x處于待定(pending)狀態(tài),promise必須保持待定(pending)狀態(tài)直至x兌現(xiàn)或者拒絕.
如果x處于已兌現(xiàn)(fulfilled)狀態(tài),使用與x相同的終值兌現(xiàn)promise.
如果x處于已拒絕(rejected)狀態(tài),使用與x相同的拒因拒絕promise.
resolve_promise_x_is_promise.png
如果x不是對象或者函數(shù),以x為終值兌現(xiàn)promise.
resolve_promise_x_isnot_obj.png
如果x是對象或者函數(shù).
將x.then賦值給then.
如果在x.then的取值過程中拋出了錯誤e,則以e為拒因拒絕promise.
resolve_promise_get_then.png
如果then不是函數(shù),以x為終值兌現(xiàn)promise.
resolve_promise_then_isnot_function.png
如果then是函數(shù),則將x作為函數(shù)的作用域this調(diào)用它,第一個參數(shù)叫做resolvePromise,第二個參數(shù)叫做rejectPromise.
如果resolvePromise以y為值調(diào)用,運(yùn)行[[Resolve]](promise, y).
如果rejectPromise以r為拒因調(diào)用,promise以r為拒因拒絕.
resolve_promise_then_is_function.png
如果resolvePromise和rejectPromise都被調(diào)用,或者同一個參數(shù)有多處調(diào)用,則第一個調(diào)用將被執(zhí)行,其他調(diào)用將被忽略.
resolve_promise_called.png
如果then函數(shù)拋出了錯誤e:
a.如果resolvePromise或者rejectPromise已被調(diào)用,則忽略錯誤.
b.否則,將e作為拒因拒絕promise.
resolve_promise_then_err.png
Promise解決過程全貌如下:
promise_resolve_all.png
至此,Promise/A+已手動實(shí)現(xiàn).讓我們做一個簡單的運(yùn)行測試.
promise_plus_excute.png
可以看到手動實(shí)現(xiàn)的PromiseAPlus與原生的Promise有些許差別.原生Promise中的回調(diào)要先于setTimeout中的回調(diào)調(diào)用.這種表現(xiàn)涉及到宏任務(wù)與微任務(wù)的概念,此文不詳細(xì)講述.
附PromiseAPlus實(shí)現(xiàn):
class PromiseAPlus {
private _state: PromiseState = PromiseState.pending;
private _promiseResolveValue;
private _promiseRejectReson;
private _onFulfilledParameters: Function[] = [];
private _onRejectedParameters: Function[] = [];
public constructor(excutor) {
excutor(this.resolve.bind(this), this.reject.bind(this));
}
private resolve(value) {
const ts = this;
if (ts._state != PromiseState.pending) {
throw new Error("MyPromise is fulfilled or rejected");
}
ts._promiseResolveValue = value;
ts._state = PromiseState.fulfilled;
setTimeout(() => {
ts._onFulfilledParameters
.forEach((onFulfilled) => {
onFulfilled();
});
}, 0);
}
private reject(reson) {
const ts = this;
if (this._state != PromiseState.pending) {
throw new Error("MyPromise is fulfilled or rejected");
}
ts._promiseRejectReson = reson;
ts._state = PromiseState.rejected;
setTimeout(() => {
ts._onRejectedParameters
.forEach((onRejected) => {
onRejected();
});
}, 0);
}
public then(onFulfilled?: (value: any) => any, onRejected?: (reson: any) => any) {
const ts = this;
let promise = new PromiseAPlus((resolve, reject) => {
switch (ts._state) {
case PromiseState.fulfilled:
setTimeout(() => {
ts._onFulfilledF(promise, onFulfilled, resolve, reject);
}, 0);
break;
case PromiseState.rejected:
setTimeout(() => {
ts._onRejectedF(promise, onFulfilled, resolve, reject);
}, 0);
break;
case PromiseState.pending:
ts._onFulfilledParameters.push(() => {
ts._onFulfilledF(promise, onFulfilled, resolve, reject);
});
ts._onRejectedParameters.push(() => {
ts._onRejectedF(promise, onFulfilled, resolve, reject);
});
break;
}
});
return promise;
}
private _onFulfilledF(promise: PromiseAPlus, onFulfilled: onFulfilled, resolve: (value) => any, reject: (reson: any) => any): void {
if (typeof onFulfilled != "function") {
resolve(this._promiseResolveValue);
return;
}
let x: any, hasErr: boolean;
try {
x = onFulfilled(this._promiseResolveValue);
} catch (err) {
hasErr = !!err;
reject(err);
}
!hasErr && this._resolvePromiseF(promise, x, resolve, reject);
}
private _onRejectedF(promise: PromiseAPlus, onRejected: onRejected, resolve: (value) => any, reject: (reson: any) => any): void {
if (typeof onRejected != "function") {
reject(this._promiseRejectReson);
return;
}
let x: any, hasErr: boolean;
try {
x = onRejected(this._promiseRejectReson);
} catch (err) {
hasErr = !!err;
reject(err);
}
!hasErr && this._resolvePromiseF(promise, x, resolve, reject);
}
private _resolvePromiseF(promise: PromiseAPlus, x: any, resolve: (value: any) => any, reject: (reson: any) => any): void {
if (promise === x) {
reject(new TypeError("promise and x refer to same value"));
return;
}
if (x instanceof PromiseAPlus) {
x.then(resolve, reject);
return;
}
if (typeof x != "object" && typeof x != "function") {
resolve(x);
return;
}
let then;
try {
then = x.then;
} catch (err) {
reject(err);
return;
}
if (typeof then != "function") {
resolve(x);
return;
}
let called: boolean;
try {
then.call(x, y => {
if (called) {
return;
}
called = true;
promise._resolvePromiseF(promise, y, resolve, reject);
}, r => {
if (called) {
return;
}
called = true;
reject(r);
});
} catch (err) {
if(called){
return;
}
reject(err);
}
}
}