摘要:對(duì)象有個(gè)屬性,一個(gè)為,方法里面注入的回調(diào)函數(shù),用來(lái)對(duì)傳入的上一個(gè)傳遞過(guò)來(lái)的值進(jìn)行處理另一個(gè)為,構(gòu)造函數(shù)內(nèi)部定義的方法,用來(lái)改變狀態(tài)以及值。通過(guò)再次對(duì)構(gòu)造函數(shù)的加強(qiáng),完成了鏈?zhǔn)秸{(diào)用的功能。
最近看了一篇關(guān)于Promise內(nèi)部實(shí)現(xiàn)原理的文章Javascript in wicked detail。作者從簡(jiǎn)明的例子入手,一步一步的構(gòu)建健壯的Promise實(shí)現(xiàn)。我就拿作者文中的代碼實(shí)例梳理下文章的核心內(nèi)容。
大家一定看到過(guò)嵌套很深回調(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ù)的時(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
首先來(lái)看一段定義簡(jiǎn)單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í)行后返回得到一個(gè)promise實(shí)例,實(shí)例上有then()方法,可以接受回調(diào)函數(shù)。
doSomething().then(function(value) { console.log(value); })
但是上面的代碼會(huì)報(bào)錯(cuò)(callback is undefined),是因?yàn)椋?strong>resolve中的callback要早于then()方法中的callback的賦值操作。
那么對(duì)Promise構(gòu)造函數(shù)稍微處理下,把同步的代碼使用setTimeout來(lái)hack下,改變代碼的執(zhí)行順序,使得resolve函數(shù)中的callback對(duì)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); }
這里通過(guò)setTimeout異步函數(shù)改變了代碼執(zhí)行的順序,確保callback被調(diào)用前已經(jīng)被賦值成cb。
重新調(diào)用:
doSomething().then(function(value) { console.log(value); }) // 42 //正常執(zhí)行。
但是定義Promise構(gòu)造函數(shù)的代碼還是有問題的,因?yàn)槿绻麅H僅是調(diào)用then()方法而注入回調(diào)的話,內(nèi)部的callback仍然是null。同樣不能正常的執(zhí)行。
別急,慢慢來(lái)。
Promises have state事實(shí)上Promise是有狀態(tài)的:
pending
resolved
rejected
pending => resolved 或者 pending => rejected。狀態(tài)一旦發(fā)生改變,不可逆。接下來(lái),讓我們?cè)?b>Promise的構(gòu)造函數(shù)里面加入state,使用state來(lái)控制整個(gè)代碼流程。
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來(lái)控制代碼的流程。then()方法和resolve()方法將控制權(quán)交給了新的方法handle(),由handle()方法來(lái)根據(jù)state的值進(jìn)行流程操作:
如果state為pending狀態(tài),即在resolve()之前調(diào)用了then()方法,那么會(huì)將onResolved回調(diào)賦值給一個(gè)deferred延遲對(duì)象,deferred對(duì)象將這個(gè)回調(diào)保存起來(lái),稍后當(dāng)resolve()調(diào)用時(shí),pending狀態(tài)變?yōu)?b>resolved,并調(diào)用deferred對(duì)象。
如果在then()方法前調(diào)用resolve()方法,pending狀態(tài)變?yōu)?b>resolved,然后調(diào)用then()里面注入的回調(diào)onResolved.
通過(guò)以上的代碼,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()方法,那么只有最后一個(gè)then()方法里面注入的callback才會(huì)有用。解決這個(gè)問題的方法就是維持一個(gè)deferreds隊(duì)列,去保存每次then()方法注入的回調(diào)函數(shù)。
Chaining Promises下面的代碼是最普通不過(guò)的promise鏈?zhǔn)秸{(diào)用:
getSomeData() .then(filterTheData) .then(processTheData) .then(displayTheData)
getSomeData()方法調(diào)用后會(huì)返回一個(gè)promise對(duì)象,這樣便可以調(diào)用then()方法,同樣這第一個(gè)then()方法調(diào)用后也會(huì)返回一個(gè)promise對(duì)象。這樣才能繼續(xù)調(diào)用then()方法。
then()方法總是返回一個(gè)promise。
接下來(lái)在代碼中加以實(shí)現(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()方法后會(huì)返回一個(gè)新的生成的promise對(duì)象。它具有then()方法,可以繼續(xù)調(diào)用then(),并返回一個(gè)新生成的promise對(duì)象。如此繼續(xù)進(jìn)行下去。這就實(shí)現(xiàn)了Promise鏈?zhǔn)秸{(diào)用。
再來(lái)看看具體的代碼實(shí)現(xiàn):
resolve()方法沒什么變化,但是handle()方法接收一個(gè)handler對(duì)象。handler對(duì)象有2個(gè)屬性,一個(gè)為onResolved,then()方法里面注入的回調(diào)函數(shù),用來(lái)對(duì)傳入的上一個(gè)promise傳遞過(guò)來(lái)的值進(jìn)行處理;另一個(gè)為resolve,Promise構(gòu)造函數(shù)內(nèi)部定義的resolve()方法,用來(lái)改變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()方法新建一個(gè)promise對(duì)象過(guò)程當(dāng)中,handle({onResolved: onResolved, resolve: resolve})中resolve屬性始終是獲得的定義過(guò)程中對(duì)外部resolve方法的引用。即上一次的promise中定義的resolve.
當(dāng)then()方法里面注入回調(diào)函數(shù)時(shí),調(diào)用onResolved方法并獲得返回值ret,傳入resolve方法,改變state的值以及更改promise中需要繼續(xù)傳遞下去的值。如果onResolved方法中會(huì)返回處理過(guò)的值,那么下一個(gè)promise能拿到這個(gè)值,如果onResolved沒有返回,傳入下一個(gè)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ù)時(shí),仍然會(huì)調(diào)用resolve方法,改變state的值,以及獲取上一個(gè)promise傳遞過(guò)來(lái)的值,并將值傳遞給下一個(gè)promise。
doSomething().then().then(function(result) { console.log("Got a result", result); }); //the output is // //Got a result 42
主要是得益于handle()方法中,調(diào)用resolve方法獲取從上一個(gè)promise得到的value以及作為傳入下一個(gè)promise的value:
if(!handler.onResolved) { handler.resolve(value); return; }
再每次調(diào)用then()方法的過(guò)程都會(huì)新建一個(gè)pending狀態(tài)的promise,并通過(guò)resolve方法改變狀態(tài),如果then()方法中注入了回調(diào)函數(shù),并返回了值,那么這個(gè)值會(huì)一直傳遞下去,如果沒有注入回調(diào)函數(shù),resolve方法會(huì)獲取上一個(gè)promise傳遞過(guò)來(lái)的值,并作為傳入下一個(gè)promise的值。即then()方法注入的回調(diào)函數(shù)是可選的。
通過(guò)再次對(duì)Promise構(gòu)造函數(shù)的加強(qiáng),完成了promise鏈?zhǔn)秸{(diào)用的功能。
對(duì)于reject的部分過(guò)2天加上。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/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...
閱讀 2847·2021-09-28 09:45
閱讀 1511·2021-09-26 10:13
閱讀 913·2021-09-04 16:45
閱讀 3671·2021-08-18 10:21
閱讀 1099·2019-08-29 15:07
閱讀 2642·2019-08-29 14:10
閱讀 3154·2019-08-29 13:02
閱讀 2471·2019-08-29 12:31