摘要:對象有個屬性,一個為,方法里面注入的回調(diào)函數(shù),用來對傳入的上一個傳遞過來的值進(jìn)行處理另一個為,構(gòu)造函數(shù)內(nèi)部定義的方法,用來改變狀態(tài)以及值。通過再次對構(gòu)造函數(shù)的加強(qiáng),完成了鏈?zhǔn)秸{(diào)用的功能。
最近看了一篇關(guān)于Promise內(nèi)部實現(xiàn)原理的文章Javascript in wicked detail。作者從簡明的例子入手,一步一步的構(gòu)建健壯的Promise實現(xiàn)。我就拿作者文中的代碼實例梳理下文章的核心內(nèi)容。
大家一定看到過嵌套很深回調(diào)函數(shù),那么如何在保證代碼流程才能將這些縱向嵌套的代碼變成橫向偏平的呢?
doSomething(function(value) { console.log("Got a value:" + value); })
to this
doSomething().then(function(value) { console.log("Got a value:" + value); })
那么我們就應(yīng)該在定義doSomething函數(shù)的時候做出相應(yīng)的變化
function doSomething(callback) { var value = 42; callback(value); }
to this
function doSomething() { return { then: function(callback) { var value = 42; callback(42); } } }Defining the Promise type
首先來看一段定義簡單Promise構(gòu)造函數(shù)的代碼:
function Promise(fn) { var callback = null; this.then = function(cb) { callback = cb; } function resolve(value) { callback(value) } fn(resolve); }
然后重寫doSomething()函數(shù):
function doSomething() { return new Promise(function(resolve) { var value = 42; resolve(value); }) }
重新定義后的doSomething()函數(shù)執(zhí)行后返回得到一個promise實例,實例上有then()方法,可以接受回調(diào)函數(shù)。
doSomething().then(function(value) { console.log(value); })
但是上面的代碼會報錯(callback is undefined),是因為:resolve中的callback要早于then()方法中的callback的賦值操作。
那么對Promise構(gòu)造函數(shù)稍微處理下,把同步的代碼使用setTimeout來hack下,改變代碼的執(zhí)行順序,使得resolve函數(shù)中的callback對value進(jìn)行處理前被賦值了。
function Promise(fn) { var callback = null; this.then = function(cb) { callback = cb; } function resolve(value) { setTimeout(function() { callback(value); }, 1) } fn(resolve); }
這里通過setTimeout異步函數(shù)改變了代碼執(zhí)行的順序,確保callback被調(diào)用前已經(jīng)被賦值成cb。
重新調(diào)用:
doSomething().then(function(value) { console.log(value); }) // 42 //正常執(zhí)行。
但是定義Promise構(gòu)造函數(shù)的代碼還是有問題的,因為如果僅僅是調(diào)用then()方法而注入回調(diào)的話,內(nèi)部的callback仍然是null。同樣不能正常的執(zhí)行。
別急,慢慢來。
Promises have state事實上Promise是有狀態(tài)的:
pending
resolved
rejected
pending => resolved 或者 pending => rejected。狀態(tài)一旦發(fā)生改變,不可逆。接下來,讓我們在Promise的構(gòu)造函數(shù)里面加入state,使用state來控制整個代碼流程。
function Promise(fn) { var state = "pending", value, deferred; function resolve(newValue) { state = "resolved"; value = newValue; if(deferred) { handle(deferred); } } function handle(onResolved) { if(state === "pending") { deferred = onResolved; return ; } onResolved(value); } this.then = function(onResolved) { handle(onResolved); } fn(resolve); }
代碼變的比之前更加復(fù)雜。但是現(xiàn)在使用state來控制代碼的流程。then()方法和resolve()方法將控制權(quán)交給了新的方法handle(),由handle()方法來根據(jù)state的值進(jìn)行流程操作:
如果state為pending狀態(tài),即在resolve()之前調(diào)用了then()方法,那么會將onResolved回調(diào)賦值給一個deferred延遲對象,deferred對象將這個回調(diào)保存起來,稍后當(dāng)resolve()調(diào)用時,pending狀態(tài)變?yōu)?b>resolved,并調(diào)用deferred對象。
如果在then()方法前調(diào)用resolve()方法,pending狀態(tài)變?yōu)?b>resolved,然后調(diào)用then()里面注入的回調(diào)onResolved.
通過以上的代碼,promise可以任意次數(shù)的調(diào)用then()方法:
var promise = doSomething(); promise.then(function(value) { console.log("Got a value:", value); }); // 42 promise.then(function(value) { console.log("Got the some value again:", value); }); //42
但是這樣的Promise構(gòu)造函數(shù)還是有問題的,大家可以想象下,在調(diào)用resolve()方法前,調(diào)用了很多次的then()方法,那么只有最后一個then()方法里面注入的callback才會有用。解決這個問題的方法就是維持一個deferreds隊列,去保存每次then()方法注入的回調(diào)函數(shù)。
Chaining Promises下面的代碼是最普通不過的promise鏈?zhǔn)秸{(diào)用:
getSomeData() .then(filterTheData) .then(processTheData) .then(displayTheData)
getSomeData()方法調(diào)用后會返回一個promise對象,這樣便可以調(diào)用then()方法,同樣這第一個then()方法調(diào)用后也會返回一個promise對象。這樣才能繼續(xù)調(diào)用then()方法。
then()方法總是返回一個promise。
接下來在代碼中加以實現(xiàn):
function Promise(fn) { var state = "pending", value, deferred = null; function resolve(newValue) { state = "resolved"; value = newValue; if(deferred) { handle(deferred); } } function handle(handler) { if(state == "pending") { deferred = handler; return; } if(!handler.onResolved) { handler.resolve(value); return; } var ret = handler.onResolved(value); handler.resolve(ret); } this.then = function(onResolved) { return new Promise(function(resolve) { handle({ onResolved: onResolved, resolve: resolve }); }); }; fn(resolve); }
在這次的代碼中,調(diào)用then()方法后會返回一個新的生成的promise對象。它具有then()方法,可以繼續(xù)調(diào)用then(),并返回一個新生成的promise對象。如此繼續(xù)進(jìn)行下去。這就實現(xiàn)了Promise鏈?zhǔn)秸{(diào)用。
再來看看具體的代碼實現(xiàn):
resolve()方法沒什么變化,但是handle()方法接收一個handler對象。handler對象有2個屬性,一個為onResolved,then()方法里面注入的回調(diào)函數(shù),用來對傳入的上一個promise傳遞過來的值進(jìn)行處理;另一個為resolve,Promise構(gòu)造函數(shù)內(nèi)部定義的resolve()方法,用來改變Promise狀態(tài)以及value值。
具體分析下handle()函數(shù):
function handle(handler) { if(state === "pending") { deferred = handler; return; } if(!handler.onResolved) { handler.resolve(value); return; } var ret = handler.onResolved(value); handler.resolve(ret); }
每次調(diào)用then()方法新建一個promise對象過程當(dāng)中,handle({onResolved: onResolved, resolve: resolve})中resolve屬性始終是獲得的定義過程中對外部resolve方法的引用。即上一次的promise中定義的resolve.
當(dāng)then()方法里面注入回調(diào)函數(shù)時,調(diào)用onResolved方法并獲得返回值ret,傳入resolve方法,改變state的值以及更改promise中需要繼續(xù)傳遞下去的值。如果onResolved方法中會返回處理過的值,那么下一個promise能拿到這個值,如果onResolved沒有返回,傳入下一個promise的為undefined**
doSomething().then(function(result) { console.log("First result", result); return 88; }).then(function(secondResult) { console.log("second result", secondResult); }) //the output is // //First result 42 //Second result 88 doSomething().then(function(result) { console.log("First result", result); }).then(function(secondResult) { console.log("Second result", secondResult); }) //now the output is //First result 42 //Second result undefined
當(dāng)then()沒有注入回調(diào)函數(shù)時,仍然會調(diào)用resolve方法,改變state的值,以及獲取上一個promise傳遞過來的值,并將值傳遞給下一個promise。
doSomething().then().then(function(result) { console.log("Got a result", result); }); //the output is // //Got a result 42
主要是得益于handle()方法中,調(diào)用resolve方法獲取從上一個promise得到的value以及作為傳入下一個promise的value:
if(!handler.onResolved) { handler.resolve(value); return; }
再每次調(diào)用then()方法的過程都會新建一個pending狀態(tài)的promise,并通過resolve方法改變狀態(tài),如果then()方法中注入了回調(diào)函數(shù),并返回了值,那么這個值會一直傳遞下去,如果沒有注入回調(diào)函數(shù),resolve方法會獲取上一個promise傳遞過來的值,并作為傳入下一個promise的值。即then()方法注入的回調(diào)函數(shù)是可選的。
通過再次對Promise構(gòu)造函數(shù)的加強(qiáng),完成了promise鏈?zhǔn)秸{(diào)用的功能。
對于reject的部分過2天加上。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/79158.html
摘要:,,群 Assassin-Trojan---Hack Others’ Android Devices Within 5mins showImg(https://segmentfault.com/img/remote/1460000018924873); hack the android device with only one instruction on your terminal ...
摘要: Awesome JavaScript A collection of awesome browser-side JavaScript libraries, resources and shiny things. Awesome JavaScript Package Managers Loaders Testing Frameworks QA Tools MVC Framew...
閱讀 2837·2021-09-28 09:45
閱讀 1510·2021-09-26 10:13
閱讀 905·2021-09-04 16:45
閱讀 3665·2021-08-18 10:21
閱讀 1092·2019-08-29 15:07
閱讀 2637·2019-08-29 14:10
閱讀 3150·2019-08-29 13:02
閱讀 2465·2019-08-29 12:31