成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

[轉(zhuǎn)載·JS] JavaScript Promise啟示錄

Lyux / 2514人閱讀

摘要:近幾年隨著開發(fā)模式的逐漸成熟,規(guī)范順勢而生,其中就包括提出了規(guī)范,完全改變了異步編程的寫法,讓異步編程變得十分的易于理解。最后,是如此的優(yōu)雅但也只是解決了回調(diào)的深層嵌套的問題,真正簡化異步編程的還是,在端,建議考慮。

前段時(shí)間頻頻看到Promise這個(gè)詞,今天發(fā)現(xiàn)騰訊AlloyTeam寫得這篇很贊,遂轉(zhuǎn)之。 原文鏈接

本篇,主要普及promise的用法。

一直以來,JavaScript處理異步都是以callback的方式,在前端開發(fā)領(lǐng)域callback機(jī)制幾乎深入人心。在設(shè)計(jì)API的時(shí)候,不管是瀏覽器廠商還是SDK開發(fā)商亦或是各種類庫的作者,基本上都已經(jīng)遵循著callback的套路。

近幾年隨著JavaScript開發(fā)模式的逐漸成熟,CommonJS規(guī)范順勢而生,其中就包括提出了Promise規(guī)范,Promise完全改變了js異步編程的寫法,讓異步編程變得十分的易于理解。

在callback的模型里邊,我們假設(shè)需要執(zhí)行一個(gè)異步隊(duì)列,代碼看起來可能像這樣:

loadImg("a.jpg", function() {
    loadImg("b.jpg", function() {
        loadImg("c.jpg", function() {
            console.log("all done!");
        });
    });
});

這也就是我們常說的回調(diào)金字塔,當(dāng)異步的任務(wù)很多的時(shí)候,維護(hù)大量的callback將是一場災(zāi)難。當(dāng)今Node.js大熱,好像很多團(tuán)隊(duì)都要用它來做點(diǎn)東西以沾沾“洋氣”,曾經(jīng)跟一個(gè)運(yùn)維的同學(xué)聊天,他們也是打算使用Node.js做一些事情,可是一想到j(luò)s的層層回調(diào)就望而卻步。

好,扯淡完畢,下面進(jìn)入正題。

Promise可能大家都不陌生,因?yàn)镻romise規(guī)范已經(jīng)出來好一段時(shí)間了,同時(shí)Promise也已經(jīng)納入了ES6,而且高版本的chrome、firefox瀏覽器都已經(jīng)原生實(shí)現(xiàn)了Promise,只不過和現(xiàn)如今流行的類Promise類庫相比少些API。

所謂Promise,字面上可以理解為“承諾”,就是說A調(diào)用B,B返回一個(gè)“承諾”給A,然后A就可以在寫計(jì)劃的時(shí)候這么寫:當(dāng)B返回結(jié)果給我的時(shí)候,A執(zhí)行方案S1,反之如果B因?yàn)槭裁丛驔]有給到A想要的結(jié)果,那么A執(zhí)行應(yīng)急方案S2,這樣一來,所有的潛在風(fēng)險(xiǎn)都在A的可控范圍之內(nèi)了。

上面這句話,翻譯成代碼類似:

var resB = B();
var runA = function() {
    resB.then(execS1, execS2);
};
runA();

只看上面這行代碼,好像看不出什么特別之處。但現(xiàn)實(shí)情況可能比這個(gè)復(fù)雜許多,A要完成一件事,可能要依賴不止B一個(gè)人的響應(yīng),可能需要同時(shí)向多個(gè)人詢問,當(dāng)收到所有的應(yīng)答之后再執(zhí)行下一步的方案。最終翻譯成代碼可能像這樣:

var resB = B();
var resC = C();
...
 
var runA = function() {
    reqB
        .then(resC, execS2)
        .then(resD, execS3)
        .then(resE, execS4)
        ...
        .then(execS1);
};
 
runA();

在這里,當(dāng)每一個(gè)被詢問者做出不符合預(yù)期的應(yīng)答時(shí)都用了不同的處理機(jī)制。事實(shí)上,Promise規(guī)范沒有要求這樣做,你甚至可以不做任何的處理(即不傳入then的第二個(gè)參數(shù))或者統(tǒng)一處理。

好了,下面我們來認(rèn)識(shí)下Promise/A+規(guī)范:

一個(gè) promise 可能有三種狀態(tài):等待(pending)、已完成(fulfilled)、已拒絕(rejected)

一個(gè) promise 的狀態(tài)只可能從“等待”轉(zhuǎn)到“完成”態(tài)或者“拒絕”態(tài),不能逆向轉(zhuǎn)換,同時(shí)“完成”態(tài)和“拒絕”態(tài)不能相互轉(zhuǎn)換

promise 必須實(shí)現(xiàn)then方法(可以說,then 就是 promise 的核心),而且then 必須返回一個(gè) promise,同一個(gè) promise 的 then 可以調(diào)用多次,并且回調(diào)的執(zhí)行順序跟它們被定義時(shí)的順序一致

then 方法接受兩個(gè)參數(shù),第一個(gè)參數(shù)是成功時(shí)的回調(diào),在 promise 由“等待”態(tài)轉(zhuǎn)換到“完成”態(tài)時(shí)調(diào)用,另一個(gè)是失敗時(shí)的回調(diào),在 promise 由“等待”態(tài)轉(zhuǎn)換到“拒絕”態(tài)時(shí)調(diào)用。同時(shí),then 可以接受另一個(gè) promise 傳入,也接受一個(gè)“類then”的對(duì)象或方法,即 thenable 對(duì)象。

可以看到,Promise規(guī)范的內(nèi)容并不算多,大家可以試著自己實(shí)現(xiàn)以下Promise。

以下是筆者自己在參考許多類Promise庫之后簡單實(shí)現(xiàn)的一個(gè)Promise,代碼請移步PromiseA.

簡單分析下思路:

構(gòu)造函數(shù)Promise接受一個(gè)函數(shù)resolver,可以理解為傳入一個(gè)異步任務(wù),resolver接受兩個(gè)參數(shù),一個(gè)是成功時(shí)的回調(diào),一個(gè)是失敗時(shí)的回調(diào),這兩參數(shù)和通過then傳入的參數(shù)是對(duì)等的。

其次是then的實(shí)現(xiàn),由于Promise要求then必須返回一個(gè)promise,所以在then調(diào)用的時(shí)候會(huì)新生成一個(gè)promise,掛在當(dāng)前promise的next上,同一個(gè)promise多次調(diào)用都只會(huì)返回之前生成的next。

由于then方法接受的兩個(gè)參數(shù)都是可選的,而且類型也沒限制,可以是函數(shù),也可以是一個(gè)具體的值,還可以是另一個(gè)promise。下面是then的具體實(shí)現(xiàn):

Promise.prototype.then = function(resolve, reject) {
    var next = this._next || (this._next = Promise());
    var status = this.status;
    var x;
 
    if("pending" === status) {
        isFn(resolve) && this._resolves.push(resolve);
        isFn(reject) && this._rejects.push(reject);
        return next;
    }
 
    if("resolved" === status) {
        if(!isFn(resolve)) {
            next.resolve(resolve);
        } else {
            try {
                x = resolve(this.value);
                resolveX(next, x);
            } catch(e) {
                this.reject(e);
            }
        }
        return next;
    }
 
    if("rejected" === status) {
        if(!isFn(reject)) {
            next.reject(reject);
        } else {
            try {
                x = reject(this.reason);
                resolveX(next, x);
            } catch(e) {
                this.reject(e);
            }
        }
        return next;
    }
};

這里,then做了簡化,其他promise類庫的實(shí)現(xiàn)比這個(gè)要復(fù)雜得多,同時(shí)功能也更多,比如還有第三個(gè)參數(shù)——notify,表示promise當(dāng)前的進(jìn)度,這在設(shè)計(jì)文件上傳等時(shí)很有用。對(duì)then的各種參數(shù)的處理是最復(fù)雜的部分,有興趣的同學(xué)可以參看其他類Promise庫的實(shí)現(xiàn)。

在then的基礎(chǔ)上,應(yīng)該還需要至少兩個(gè)方法,分別是完成promise的狀態(tài)從pending到resolved或rejected的轉(zhuǎn)換,同時(shí)執(zhí)行相應(yīng)的回調(diào)隊(duì)列,即resolve()和reject()方法。

到此,一個(gè)簡單的promise就設(shè)計(jì)完成了,下面簡單實(shí)現(xiàn)下兩個(gè)promise化的函數(shù):

function sleep(ms) {
    return function(v) {
        var p = Promise();
 
        setTimeout(function() {
            p.resolve(v);
        }, ms);
 
        return p;
    };
};
 
function getImg(url) {
    var p = Promise();
    var img = new Image();
 
    img.onload = function() {
        p.resolve(this);
    };
 
    img.onerror = function(err) {
        p.reject(err);
    };
 
    img.url = url;
 
    return p;
};

由于Promise構(gòu)造函數(shù)接受一個(gè)異步任務(wù)作為參數(shù),所以getImg還可以這樣調(diào)用:

function getImg(url) {
    return Promise(function(resolve, reject) {
        var img = new Image();
 
        img.onload = function() {
            resolve(this);
        };
 
        img.onerror = function(err) {
            reject(err);
        };
 
        img.url = url;
    });
};

接下來(見證奇跡的時(shí)刻),假設(shè)有一個(gè)BT的需求要這么實(shí)現(xiàn):異步獲取一個(gè)json配置,解析json數(shù)據(jù)拿到里邊的圖片,然后按順序隊(duì)列加載圖片,每張圖片加載時(shí)給個(gè)loading效果。

function addImg(img) {
    $("#list").find("> li:last-child").html("").append(img);
};
 
function prepend() {
    $("
  • ") .html("loading...") .appendTo($("#list")); }; function run() { $("#done").hide(); getData("map.json") .then(function(data) { $("h4").html(data.name); return data.list.reduce(function(promise, item) { return promise .then(prepend) .then(sleep(1000)) .then(function() { return getImg(item.url); }) .then(addImg); }, Promise.resolve()); }) .then(sleep(300)) .then(function() { $("#done").show(); }); }; $("#run").on("click", run);
  • 這里的sleep只是為了看效果加的,可猛擊查看demo!當(dāng)然,Node.js的例子可查看這里。

    在這里,Promise.resolve(v)靜態(tài)方法只是簡單返回一個(gè)以v為肯定結(jié)果的promise,v可不傳入,也可以是一個(gè)函數(shù)或者是一個(gè)包含then方法的對(duì)象或函數(shù)(即thenable)。

    類似的靜態(tài)方法還有Promise.cast(promise),生成一個(gè)以promise為肯定結(jié)果的promise;

    Promise.reject(reason),生成一個(gè)以reason為否定結(jié)果的promise。

    我們實(shí)際的使用場景可能很復(fù)雜,往往需要多個(gè)異步的任務(wù)穿插執(zhí)行,并行或者串行同在。這時(shí)候,可以對(duì)Promise進(jìn)行各種擴(kuò)展,比如實(shí)現(xiàn)Promise.all(),接受promises隊(duì)列并等待他們完成再繼續(xù),再比如Promise.any(),promises隊(duì)列中有任何一個(gè)處于完成態(tài)時(shí)即觸發(fā)下一步操作。

    標(biāo)準(zhǔn)的Promise

    可參考html5rocks的這篇文章JavaScript Promises,目前高級(jí)瀏覽器如chrome、firefox都已經(jīng)內(nèi)置了Promise對(duì)象,提供更多的操作接口,比如Promise.all(),支持傳入一個(gè)promises數(shù)組,當(dāng)所有promises都完成時(shí)執(zhí)行then,還有就是更加友好強(qiáng)大的異常捕獲,應(yīng)對(duì)日常的異步編程,應(yīng)該足夠了。

    第三方庫的Promise

    現(xiàn)今流行的各大js庫,幾乎都不同程度的實(shí)現(xiàn)了Promise,如dojo,jQuery、Zepto、when.js、Q等,只是暴露出來的大都是Deferred對(duì)象,以jQuery(Zepto類似)為例,實(shí)現(xiàn)上面的getImg():

    function getImg(url) {
        var def = $.Deferred();
        var img = new Image();
     
        img.onload = function() {
            def.resolve(this);
        };
     
        img.onerror = function(err) {
            def.reject(err);
        };
     
        img.src = url;
     
        return def.promise();
    };

    當(dāng)然,jQuery中,很多的操作都返回的是Deferred或promise,如animate、ajax:

    // animate
    $(".box")
        .animate({"opacity": 0}, 1000)
        .promise()
        .then(function() {
            console.log("done");
        });
     
    // ajax
    $.ajax(options).then(success, fail);
    $.ajax(options).done(success).fail(fail);
     
    // ajax queue
    $.when($.ajax(options1), $.ajax(options2))
        .then(function() {
            console.log("all done.");
        }, function() {
            console.error("There something wrong.");
        });

    Query還實(shí)現(xiàn)了done()和fail()方法,其實(shí)都是then方法的shortcut。

    處理promises隊(duì)列,jQuery實(shí)現(xiàn)的是$.when()方法,用法和Promise.all()類似。

    其他類庫,這里值得一提的是when.js,本身代碼不多,完整實(shí)現(xiàn)Promise,同時(shí)支持browser和Node.js,而且提供更加豐富的API,是個(gè)不錯(cuò)的選擇。這里限于篇幅,不再展開。

    尾聲

    我們看到,不管Promise實(shí)現(xiàn)怎么復(fù)雜,但是它的用法卻很簡單,組織的代碼很清晰,從此不用再受callback的折磨了。

    最后,Promise是如此的優(yōu)雅!但Promise也只是解決了回調(diào)的深層嵌套的問題,真正簡化JavaScript異步編程的還是Generator,在Node.js端,建議考慮Generator。

    下一篇,研究下Generator。

    github原文: https://github.com/chemdemo/chemdemo.github.io/issues/6

    參考文獻(xiàn)

    JavaScript Promises

    JavaScript Promises(中文)

    when.js

    Asynch JS: The Power Of $.Deferred

    jQuery: $.Deferred()

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

    轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/85902.html

    相關(guān)文章

    • JavaScript Promise示錄

      摘要:近幾年隨著開發(fā)模式的逐漸成熟,規(guī)范順勢而生,其中就包括提出了規(guī)范,完全改變了異步編程的寫法,讓異步編程變得十分的易于理解。最后,是如此的優(yōu)雅但也只是解決了回調(diào)的深層嵌套的問題,真正簡化異步編程的還是,在端,建議考慮。 本篇,簡單實(shí)現(xiàn)一個(gè)promise,主要普及promise的用法。 一直以來,JavaScript處理異步都是以callback的方式,在前端開發(fā)領(lǐng)域callback機(jī)制...

      Juven 評(píng)論0 收藏0
    • React組件模型示錄

      摘要:另一種關(guān)于組件的常見說法,是組件是為了重用。這件事情是前端特有的,受限制于的結(jié)構(gòu)。這一節(jié)的題目叫做混亂的組件通訊,我們來仔細(xì)掰扯一下細(xì)節(jié),因?yàn)榻M件模型雖然很常說但是對(duì)通訊過程沒有約定。 這個(gè)話題很難寫。 但是反過來說,愛因斯坦有句名言:如果你不能把一個(gè)問題向一個(gè)六歲孩子解釋清楚,那么你不真的明白它。 所以解釋清楚一個(gè)問題的關(guān)鍵,不是去擴(kuò)大化,而是相反,最小化。 Lets begin. ...

      eternalshallow 評(píng)論0 收藏0
    • JS魔法堂:定義頁面的Dispose方法——[before]unload事件示錄

      摘要:坑無視和是十分特殊的事件,要求事件處理函數(shù)內(nèi)部不能阻塞當(dāng)前線程,而卻恰恰就會(huì)阻塞當(dāng)前線程,因此規(guī)范中以明確在和中直接無視這幾個(gè)方法的調(diào)用。 前言 ?最近實(shí)施的同事報(bào)障,說用戶審批流程后直接關(guān)閉瀏覽器,操作十余次后系統(tǒng)就報(bào)用戶會(huì)話數(shù)超過上限,咨詢4A同事后得知登陸后需要顯式調(diào)用登出API才能清理4A端,否則必然會(huì)超出會(huì)話上限。?即使在頁面上增添一個(gè)登出按鈕也無法保證用戶不會(huì)直接關(guān)掉瀏覽器...

      Chiclaim 評(píng)論0 收藏0
    • 2017-07-10 前端日報(bào)

      摘要:前端日報(bào)精選入門指南入口,輸出,加載器和插件中數(shù)據(jù)類型轉(zhuǎn)換讓我印象深刻的面試題大話大前端時(shí)代一與的組件化庖丁解牛一發(fā)布中文第期手把手教你用管理狀態(tài)上個(gè)快速編程技巧眾成翻譯中執(zhí)行順序組件解耦之道眾成翻譯組件模型啟示錄有個(gè)梨作 2017-07-10 前端日報(bào) 精選 Webpack入門指南: 入口,輸出,加載器和插件JavaScript中數(shù)據(jù)類型轉(zhuǎn)換讓我印象深刻的javascript面試題大...

      Heier 評(píng)論0 收藏0
    • 2017-06-15 前端日報(bào)

      摘要:前端日報(bào)精選十問幫你理清前端工程師及大前端團(tuán)隊(duì)的成長問題譯讀完細(xì)則之后學(xué)到的件事掘金怎么寫一個(gè)組件庫一眾成翻譯還有這操作一個(gè)能生成思維導(dǎo)圖的開源搜索引擎知乎專欄中文前端推薦第天值得收藏的基礎(chǔ)教程知乎專欄第期沒有的一天轉(zhuǎn)載中回調(diào)地 2017-06-15 前端日報(bào) 精選 十問sofish:幫你理清前端工程師及大前端團(tuán)隊(duì)的成長問題![譯] 讀完 flexbox 細(xì)則之后學(xué)到的 11 件事 -...

      Benedict Evans 評(píng)論0 收藏0

    發(fā)表評(píng)論

    0條評(píng)論

    最新活動(dòng)
    閱讀需要支付1元查看
    <