国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript深入淺出異步編程-promise原理

morgan / 2540人閱讀

摘要:這樣得到權(quán)力回調(diào)函數(shù),當?shù)漠惒酱a執(zhí)行完畢后,由來執(zhí)行回調(diào)函數(shù)。而在平時的開發(fā)過程中,在異步編程中起到了幾乎不可替代的作用。

其實Promise本身并不具備異步的能力,而之所以這里需要多帶帶開一篇說明其原理,是因為Promise異步編程的過程中是一個不可或缺的一環(huán)。原因下面細說。

在說promise之前,有必要先說下JS中的回調(diào)方式。比如下面:

function doSomethingAfterTime(time, something) {
    setTimeout(fun, time);
}

但是這樣的回調(diào)方式有一個問題,可讀性太差。另外當回調(diào)的層次多了以后,容易陷入回調(diào)地獄。舉個例子:

function func1(cb){
    // do something
    cb();
}
function func2(cb){
    // do something
    cb();
}
function func3(cb){
    // do something
    cb();
}
// do
func1(function(){
    func2(function(){
        func3(function(){

        });
    });
});

這樣的代碼讀起來簡直就是折磨,暈死!

下面試著改進下代碼,試著將回調(diào)函數(shù)封裝起來。順便剖析下promise的原理。

Promise的原理

先來一個最簡單的。

function Promise(something){
    var callback = null;
    this.then = function(onCompelete){
        callback = onCompelete;
    };
    something(function (value){
        callback(value);
    });
}

下面是調(diào)用代碼。

// 事件1
function func1(){
    return new Promise(function(resolve){
        // do something
        setTimeout(function(){
            console.log("func1");
            resolve();
        },1000);
    });
}

func1().then(function(){
    console.log("func1 complete");
});

上面對Promise的封裝算是最簡單的版本,只是模擬了Promise的調(diào)用方法,比如then,還有Promise的構(gòu)造函數(shù)。但是這樣的封裝無法實現(xiàn)鏈式調(diào)用,鏈式調(diào)用的核心就是當調(diào)用某個方法的時候返回該對象本身或者該對象對應(yīng)class的全新對象。而對于Promise的改造也很簡單.

then方法返回Promise本身

Promoise需要支持多個callback

function Promise(something){
    var callbacks = [];
    this.then = function(onCompelete){
        callbacks.push(onCompelete);
        return this;
    };
    function resolve(value){
        callbacks.forEach(function(cb){
            cb(value);
        });
    }
    something(resolve);  
}

調(diào)用代碼如下:

func1().then(function(){
    console.log("func1 complete");
}).then(function(){
    console.log("then2");
}).then(function(){
    console.log("then3");
});

現(xiàn)在的Promise執(zhí)行上面的代碼后能夠得到正確的執(zhí)行結(jié)果,但是有一個問題,如果我們想在then方法再調(diào)用一個返回promise的方法?比如這樣:

// 事件2
function func2(){
    return new Promise(function(resolve){
        // do something
        setTimeout(function(){
            console.log("func2");
            resolve();
        },1000);
    });
}

func1().then(func2).then(function(){
    console.log("all complete");
});

輸出如下:

func1
all complete
func2

你會發(fā)現(xiàn)雖然func2成功調(diào)用了,但是輸出順序亂了,我們期望的正確輸出順序應(yīng)該是:

func1
func2
all complete

分析下問題出在哪里?問題就出在Promise中的callbacks,第一個then是在func1返回的Promise上調(diào)用的,而第二個then事實上還是在func1返回的Promise上調(diào)用的。然而我們希望的是,第二個then應(yīng)該是在func2返回的Promise調(diào)用,這時候就需要考慮如何進一步改造Promise了。

對于then傳入的onCompelete函數(shù)參數(shù),它是不知道這個函數(shù)具體是否會返回Promise,只有調(diào)用了onCompelete方法才能知道具體返回的數(shù)據(jù)。但是onCompelete是回調(diào)函數(shù),你無法直接在then中調(diào)用。因此需要考慮其他的方式。

如果then方法里面返回一個新的Promise對象呢?用這個新的Promise作為中間代理,比如這樣:

function Promise(something){
    var callbacks = [];
    this.then = function(onCompelete){
        return new Promise(function (resolve) {
            callbacks.push({
                onCompelete: onCompelete,
                resolve: resolve
            });
        });
    };
    function resolve(value){
        callbacks.forEach(function(cb){
            var ret = cb.onCompelete(value);
            cb.resolve(ret);
        })
    }
    something(resolve);  
}

但是運行的時候你會發(fā)現(xiàn)輸出順序還是沒變,還是有問題的。那么繼續(xù)分析問題出在哪里?
通過調(diào)試發(fā)現(xiàn),resolve傳入的value有可能是promise對象,而我們已經(jīng)在then方法里面返回了新的promise對象了,交由該對象作為代理了。因此resolve傳入的value如果是promise對象的話,那么就需要把當前promiseresolve處理權(quán)交出去,交給傳入的promise對象。相當于代理人把權(quán)力交還給實際應(yīng)該處理的對象。可能有點繞,我再詳細的描述下

func1返回的promisep1then返回的promisep2resolve傳入的promise對象為p3,func2返回的promise對象為p4

上面一共提到4個promise對象。

說下描說下調(diào)用順序。

首先由func1創(chuàng)建p1,然后調(diào)用then方法創(chuàng)建了p2,然后再次調(diào)用了then方法,由p2創(chuàng)建了p3p2p3都是由then創(chuàng)建的代理人。

這時候func1中的異步代碼執(zhí)行了,1秒過后由func1調(diào)用了p1resolve方法,并且將callbacks數(shù)組內(nèi)的方法依次調(diào)用,然后由cb.onCompelete(value)方法間接得到func2返回的p4,接著調(diào)用p2resolve方法將p4傳入。但是上面說了,p2只是個代理,應(yīng)該把權(quán)力交還給p4來執(zhí)行。這樣p4得到權(quán)力--回調(diào)函數(shù),當func2的異步代碼執(zhí)行完畢后,由p4來執(zhí)行回調(diào)函數(shù)。

因此resolve方法需要進行如下改造。

function resolve(value) {
    // 交還權(quán)力,并且把resolve傳過去
    if (value && (typeof value.then === "function")) {
        value.then.call(value, resolve);
        return;
    }
    callbacks.forEach(function (cb) {
        var ret = cb.onCompelete(value);
        cb.resolve(ret);
    });
}

上面的代碼就是交權(quán)的代碼。這樣完全的Promise修改如下:

function Promise(something) {
    var callbacks = [];
    this.then = function (onCompelete) {
        return new Promise(function (resolve) {
            callbacks.push({
                onCompelete: onCompelete,
                resolve: resolve
            });
        });
    };
    function resolve(value) {
        if (value && (typeof value.then === "function")) {
            value.then.call(value, resolve);
            return;
        }
        callbacks.forEach(function (cb) {
            var ret = cb.onCompelete(value);
            cb.resolve(ret);
        });
    }
    something(resolve);
}

這樣修改過后,再執(zhí)行如下代碼:

func1().then(func2).then(function () {
    console.log("all complete");
});

現(xiàn)在就能得到正確的執(zhí)行結(jié)果了。

至此,一個簡單的Promise定義完了。這時候有一個問題,如果調(diào)用then方法之前resolve已經(jīng)被執(zhí)行了怎么辦呢,豈不是永遠都得不到回調(diào)了?比如這樣:

(new Promise(function (resolve) {
    resolve();
})).then(function(){
    console.log("complete");
});

你會發(fā)現(xiàn)then里面的回調(diào)就不會執(zhí)行了。其實這時候只需要做一個小小的改動就行了。改造如下:

function Promise(something) {
    var callbacks = [];
    this.then = function (onCompelete) {
        return new Promise(function (resolve) {
            callbacks.push({
                onCompelete: onCompelete,
                resolve: resolve
            });
        });
    };
    function resolve(value) {
        if (value && (typeof value.then === "function")) {
            value.then.call(value, resolve);
            return;
        }
        setTimeout(function(){
            callbacks.forEach(function (cb) {
                var ret = cb.onCompelete(value);
                cb.resolve(ret);
            });
        },0);
    }
    something(resolve);
}

你會發(fā)現(xiàn),這里只是在resolve方法里面,將執(zhí)行的回調(diào)放入setTimeout中,并且timeout設(shè)為0。這里稍微說下原理

在第一篇中提到setTimeout類似定時器,JS內(nèi)容在執(zhí)行setTimeout的回調(diào)函數(shù)的時候使用線程調(diào)度的方式將回調(diào)函數(shù)調(diào)度到JS線程執(zhí)行。但凡涉及到線程調(diào)度那么肯定需要等待JS線程空閑的時候才能調(diào)度過來。這時候?qū)imeout設(shè)為0,相當于改變了代碼執(zhí)行順序。

在實際的開發(fā)過程中,上面的Promise代碼還是缺少了一個功能,那就是狀態(tài)管理,比如:pendingfulfilledrejected。下面的代碼繼續(xù)加入狀態(tài)管理的代碼,先添加pendingfulfilled的狀態(tài):

function Promise(something) {
    var callbacks = [];
    var state = 0;//0:pending,1:fulfilled
    var resultValue = null;
    this.then = function (onCompelete) {
        return new Promise(function (resolve) {
            handleCallBack({
                onCompelete: onCompelete,
                resolve: resolve
            });
        });
    };
    function handleCallBack(callback){
        switch(state){
            case 0:{
                callbacks.push(callback);
                break;
            }
            case 1:{
                var ret = callback.onCompelete(resultValue);
                callback.resolve(ret);
                break;
            }
            default:{
                break;
            }
        }
    }
    function resolve(value) {
        if (value && (typeof value.then === "function")) {
            value.then.call(value, resolve);
            return;
        }
        state = 1;
        resultValue = value;
        setTimeout(function(){
            callbacks.forEach(function (cb) {
                handleCallBack(cb);
            });
        },0);
    }
    something(resolve);
}

下面再繼續(xù)加入reject功能。

function Promise(something) {
    var callbacks = [];
    var state = 0;//0:pending,1:fulfilled 2:reject
    var resultValue = null;
    this.then = function (onCompelete, onReject) {
        return new Promise(function (resolve) {
            handleCallBack({
                onCompelete: onCompelete,
                resolve: resolve,
                reject: onReject
            });
        });
    };
    function handleCallBack(callback) {
        switch (state) {
            case 0: {
                callbacks.push(callback);
                break;
            }
            case 1: {
                var ret = callback.onCompelete(resultValue);
                callback.resolve(ret);
                break;
            }
            case 2: {
                if(callback.reject){
                    var ret = callback.reject(resultValue);
                }
                callback.resolve(ret);
                break;
            }
            default: {
                break;
            }
        }
    }
    function reject(error) {
        state = 2;
        resultValue = error;
        setTimeout(function () {
            callbacks.forEach(function (cb) {
                handleCallBack(cb);
            });
        }, 0);
    }
    function resolve(value) {
        if (value && (typeof value.then === "function")) {
            value.then.call(value, resolve);
            return;
        }
        state = 1;
        resultValue = value;
        setTimeout(function () {
            callbacks.forEach(function (cb) {
                handleCallBack(cb);
            });
        }, 0);
    }
    something(resolve,reject);
}

OK,通過上面一步一步對Promise進行修改,基本上是把Promise的功能完善了。

從這個上面一步一步剖析Promise原理的過程中,我們發(fā)現(xiàn),Promise本身并不提供異步功能,Promise只是對函數(shù)的回調(diào)功能進行了封裝,甚至可以理解為Promise就是一個回調(diào)代理。但是正是有了這個回調(diào)代理,使得我們的回調(diào)方式發(fā)生了徹底的改變,甚至直接影響了項目的架構(gòu)設(shè)計。而在平時的開發(fā)過程中,Promise在異步編程中起到了幾乎不可替代的作用。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/106711.html

相關(guān)文章

  • ES6-7

    摘要:的翻譯文檔由的維護很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...

    mudiyouyou 評論0 收藏0
  • JavaScript 異步

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。寫一個符合規(guī)范并可配合使用的寫一個符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個需求:在系統(tǒng)初始化時通過http獲取一個第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個接口,可通過...

    tuniutech 評論0 收藏0
  • JavaScript 工作原理之四-事件循環(huán)及異步編程的出現(xiàn)和 5 種更好的 async/await

    摘要:函數(shù)會在之后的某個時刻觸發(fā)事件定時器。事件循環(huán)中的這樣一次遍歷被稱為一個。執(zhí)行完畢并出棧。當定時器過期,宿主環(huán)境會把回調(diào)函數(shù)添加至事件循環(huán)隊列中,然后,在未來的某個取出并執(zhí)行該事件。 原文請查閱這里,略有改動。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第四章。 現(xiàn)在,我們將會通過回顧單線程環(huán)境下編程的弊端及如何克服這些困難以創(chuàng)建令人驚嘆...

    maochunguang 評論0 收藏0
  • JS筆記

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。異步編程入門的全稱是前端經(jīng)典面試題從輸入到頁面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識之 HTTP 協(xié)議 詳細介紹 HTT...

    rottengeek 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<