Promise

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ù):

  1. 該函數(shù)在Promise變?yōu)橐褍冬F(xiàn)(fulfilled)狀態(tài)后必須調(diào)用,并且將Promise的終值作為該函數(shù)的第一個參數(shù).
  2. 該函數(shù)在Promise變?yōu)橐褍冬F(xiàn)(fulfilled)狀態(tài)之前不可調(diào)用.
  3. 該函數(shù)只能被調(diào)用一次.

如果onRejected是一個函數(shù):

  1. 該函數(shù)在Promise變?yōu)橐丫芙^(rejected)狀態(tài)后必須調(diào)用,并且將Promise的拒因作為該函數(shù)的第一個參數(shù).
  2. 該函數(shù)在Promise變?yōu)橐褍冬F(xiàn)(rejected)狀態(tài)之前不可調(diào)用.
  3. 該函數(shù)只能被調(diào)用一次.

then函數(shù)可以被同一個Promise調(diào)用多次

  1. 如果Promise已兌現(xiàn),所有onFulfilled依照被注冊的順序依次執(zhí)行
  2. 如果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);
        }

    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唯鸭,一起剝皮案震驚了整個濱河市须蜗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌目溉,老刑警劉巖明肮,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缭付,居然都是意外死亡柿估,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門陷猫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秫舌,“玉大人,你說我怎么就攤上這事绣檬∽阍桑” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵河咽,是天一觀的道長钠右。 經(jīng)常有香客問我,道長忘蟹,這世上最難降的妖魔是什么飒房? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任搁凸,我火速辦了婚禮,結(jié)果婚禮上狠毯,老公的妹妹穿的比我還像新娘护糖。我一直安慰自己,他們只是感情好嚼松,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布嫡良。 她就那樣靜靜地躺著,像睡著了一般献酗。 火紅的嫁衣襯著肌膚如雪寝受。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天罕偎,我揣著相機(jī)與錄音很澄,去河邊找鬼。 笑死颜及,一個胖子當(dāng)著我的面吹牛甩苛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播俏站,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼讯蒲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肄扎?” 一聲冷哼從身側(cè)響起墨林,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎反浓,沒想到半個月后萌丈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赞哗,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雷则,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了肪笋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片月劈。...
    茶點(diǎn)故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖藤乙,靈堂內(nèi)的尸體忽然破棺而出猜揪,到底是詐尸還是另有隱情,我是刑警寧澤坛梁,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布而姐,位于F島的核電站,受9級特大地震影響划咐,放射性物質(zhì)發(fā)生泄漏拴念。R本人自食惡果不足惜钧萍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望政鼠。 院中可真熱鬧风瘦,春花似錦、人聲如沸公般。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽官帘。三九已至瞬雹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刽虹,已是汗流浹背挖炬。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留状婶,地道東北人意敛。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像膛虫,于是被迫代替她去往敵國和親草姻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容