摘要:這篇文章內(nèi)容主要來自一篇高票答案聲明此的實現(xiàn)僅僅是為了加深本人對其的理解,和規(guī)范有些出入,但是的確是目前看過所有代碼中最漂亮,思路比較清晰的一個。
這篇文章內(nèi)容主要來自一篇stack Overflow高票答案
聲明:此Promise的實現(xiàn)僅僅是為了加深本人對其的理解,和A+規(guī)范有些出入,但是的確是目前看過所有promise代碼中最漂亮,思路比較清晰的一個。
文章不會特意幫助讀者復習Promise基本操作。
Promise其實本質(zhì)上就是一個狀態(tài)機,所以首先我們描述一個靜態(tài)的狀態(tài)機,就像下邊這樣
var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise() { // 存儲的狀態(tài)是上邊的三個:執(zhí)行中,已完成,已拒絕 var state = PENDING; // 存儲異步結(jié)果或者異步錯誤消息 var value = null; // 負責處理中途加入的處理函數(shù) var handlers = []; }狀態(tài)改變
完成了基本的狀態(tài)機定義,接下來的問題就是完成“狀態(tài)改變”這個動作的實現(xiàn):
var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise() { // 存儲三個狀態(tài) var state = PENDING; // 一旦出現(xiàn)狀態(tài)的改變,異步結(jié)果就會被存到這個地方 var value = null; // 存儲成功或者失敗的handler var handlers = []; //狀態(tài)轉(zhuǎn)移到成功 function fulfill(result) { state = FULFILLED; value = result; } //狀態(tài)轉(zhuǎn)移到失敗 function reject(error) { state = REJECTED; value = error; } }
到目前為止,我們給出了兩個很純粹的變化動作,在開發(fā)的過程中這兩個動作會很不好用,所以我們在這兩個動作的基礎上構建一個高層次的動作(其實就是加點判斷然后封裝一層),就像下邊這兒,名字就叫做resolve,但是注意和我們正常使用promise調(diào)用的那個resolve并不一樣,不要搞混:
var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise() { var state = PENDING; var value = null; var handlers = []; function fulfill(result) { state = FULFILLED; value = result; } function reject(error) { state = REJECTED; value = error; } //這里暫時缺少兩個重要函數(shù)getThen和doResolve這兩個函數(shù),稍后會說道 function resolve(result) { try { var then = getThen(result); //判斷then是不是一個Promise對象 if (then) { doResolve(then.bind(result), resolve, reject) return } fulfill(result); } catch (e) { reject(e); } } }
是的,我們的用到了兩個輔助函數(shù)getThen和doResolve,現(xiàn)在給出實現(xiàn):
/** * 這里會判斷value的類型,我們只要promise.then這個函數(shù),其他的統(tǒng)統(tǒng)返回null * * @param {Promise|Any} value * @return {Function|Null} */ function getThen(value) { var t = typeof value; if (value && (t === "object" || t === "function")) { var then = value.then; if (typeof then === "function") { return then; } } return null; } /** * 這個函數(shù)的主要作用就是串主邏輯,完成“變化狀態(tài)”這個動作 * * @param {Function} fn A resolver function that may not be trusted * @param {Function} onFulfilled * @param {Function} onRejected */ function doResolve(fn, onFulfilled, onRejected) { //done的作用是讓onFulfilled或者onRejected僅僅被調(diào)用一次,狀態(tài)機狀態(tài)一旦改變沒法回頭 var done = false; try { //在我們正常使用Promise的時候調(diào)的resolve,其實用的就是這里fn注入函數(shù)然后調(diào)用 fn(function (value) { if (done) return done = true **onFulfilled(value)** }, function (reason) { if (done) return done = true onRejected(reason) }) } catch (ex) { if (done) return done = true onRejected(ex) } }構建
好了,一個完整的狀態(tài)機已經(jīng)完成,我們完成了一個基本的狀態(tài)變化邏輯,接下來要做的就是一步一步的朝promise標準進發(fā),這個promise缺少什么呢,暫時缺的就是初始的動作啦(new promise(func)對象一旦被初始化內(nèi)部代碼立即執(zhí)行),所以我們加上初始動作的開啟
var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise(fn) { var state = PENDING; var value = null; var handlers = []; function fulfill(result) { state = FULFILLED; value = result; } function reject(error) { state = REJECTED; value = error; } function resolve(result) { try { var then = getThen(result); if (then) { doResolve(then.bind(result), resolve, reject) return } fulfill(result); } catch (e) { reject(e); } } //開啟任務的執(zhí)行,所以我說doResolve其實才是“主線任務”的引子,而fn其實就是你寫的代碼 doResolve(fn, resolve, reject); }聯(lián)動
我們實現(xiàn)了狀態(tài)機,但是目前的問題是我們只能眼睜睜的看著代碼的流動直到一個Promise結(jié)束為止,即沒法添加也沒法獲取結(jié)果,這就有很大的局限性了,所以我們要使用then方法來串聯(lián)Promise,用done方法來完成結(jié)果的收集,首先實現(xiàn)done方法,因為then其實說白了就是收集上邊的結(jié)果--完成自己的邏輯--把結(jié)果傳遞給下一個Promise,做的事情是done的超集。
var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise(fn) { var state = PENDING; var value = null; var handlers = []; function fulfill(result) { state = FULFILLED; value = result; //專門封裝一個handle函數(shù)處理后續(xù)邏輯,在下面有this.handle(handler)方法 handlers.forEach(handle); //在狀態(tài)變成已處理并且之前加入的handler都被處理完畢的情況下再加入handler就會報錯并且沒有卵用 handlers = null; } function reject(error) { state = REJECTED; value = error; handlers.forEach(handle); handlers = null; } function resolve(result) { try { var then = getThen(result); if (then) { doResolve(then.bind(result), resolve, reject) return } fulfill(result); } catch (e) { reject(e); } } function handle(handler) { if (state === PENDING) { handlers.push(handler); } else { if (state === FULFILLED && typeof handler.onFulfilled === "function") { handler.onFulfilled(value); } if (state === REJECTED && typeof handler.onRejected === "function") { handler.onRejected(value); } } } //注意看下面done方法的實現(xiàn),里邊只有一個異步方法,換句話說就是會立即返回不會產(chǎn)生阻塞,我們之后會在then當中調(diào)用done方法,這里的onFulfilled, onRejected就是用戶寫的處理函數(shù),promise異步的特性就是這樣來的。 this.done = function (onFulfilled, onRejected) { // ensure we are always asynchronous setTimeout(function () { handle({ onFulfilled: onFulfilled, onRejected: onRejected }); }, 0); } doResolve(fn, resolve, reject); }
最后,我們來實現(xiàn)Promise.then,完成狀態(tài)機的串聯(lián):
//這段代碼有點繞,主要需要完成的工作其實就是,判斷上一個Promise是否完成,然后執(zhí)行用戶的then里邊的回調(diào)代碼,并最終返回一個新的Promise,然后依次循環(huán)。。。 this.then = function (onFulfilled, onRejected) { //開啟then之后就會返回一個新的promise,但是這個時候我們還可能有上一個Promise的任務沒有完成,所以先把上邊一個promise對象的this指向保存下來 var self = this; //返回一個新包裝Promise,這和我們普通的在外邊寫new Promise是一個道理 return new Promise(function (resolve, reject) { //done的代碼同樣是立即返回,然后異步執(zhí)行的 return self.done(function (result) { if (typeof onFulfilled === "function") { try { return resolve(onFulfilled(result)); } catch (ex) { return reject(ex); } } else { return resolve(result); } }, function (error) { if (typeof onRejected === "function") { try { return resolve(onRejected(error)); } catch (ex) { return reject(ex); } } else { return reject(error); } }); }); }
Over
更多參考請看下面:
簡單的實現(xiàn)Promsie
高性能實現(xiàn)Promise,以及專門的wiki
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107272.html
摘要:從源碼看概念與實現(xiàn)是異步編程中的重要概念,它較好地解決了異步任務中回調(diào)嵌套的問題。這些概念中有趣的地方在于,標識狀態(tài)的變量如都是形容詞,用于傳入數(shù)據(jù)的接口如與都是動詞,而用于傳入回調(diào)函數(shù)的接口如及則在語義上用于修飾動詞的副詞。 從源碼看 Promise 概念與實現(xiàn) Promise 是 JS 異步編程中的重要概念,它較好地解決了異步任務中回調(diào)嵌套的問題。在沒有引入新的語言機制的前提下,這...
摘要:前言中的異步,剛開始的時候都是用回調(diào)函數(shù)實現(xiàn)的,所以如果異步嵌套的話,就有出現(xiàn)回調(diào)地獄,使得代碼難以閱讀和難以維護,后來出現(xiàn)了,解決了回調(diào)地獄的問題。 前言 js中的異步,剛開始的時候都是用回調(diào)函數(shù)實現(xiàn)的,所以如果異步嵌套的話,就有出現(xiàn)回調(diào)地獄,使得代碼難以閱讀和難以維護,后來es6出現(xiàn)了promise,解決了回調(diào)地獄的問題。現(xiàn)在我們就自己寫代碼實現(xiàn)一下promise,這樣才能深入理解...
摘要:讓我們先看下狀態(tài)機的概念。下面是狀態(tài)機模型中的個要素,即現(xiàn)態(tài)條件動作次態(tài)。因為訂單和審批公文都有很多的流程,每個流程都會產(chǎn)生狀態(tài)的變化,而且流程是這種業(yè)務的主軸,其他都是圍繞這個流程和狀態(tài)變化來考慮的,所以看起來蠻適合用狀態(tài)機來做。 1、背景在我打算學習spring statemachine的時候,我?guī)缀蹩催^了所有網(wǎng)上的中文教程,基本上都處于淺嘗輒止的階段,有幾篇講的比較深入的,都只是...
摘要:狀態(tài)機狀態(tài)機是模型層面的概念,與編程語言無關。狀態(tài)機具有良好的可實現(xiàn)性和可測試性。在代碼里,這是一個,但是我們在狀態(tài)機模型中要把他理解為事件。 這一篇是這個系列的開篇,沒有任何高級內(nèi)容,就講講狀態(tài)機。 狀態(tài)機 狀態(tài)機是模型層面的概念,與編程語言無關。它的目的是為對象行為建模,屬于設計范疇。它的基礎概念是狀態(tài)(state)和事件(event)。 對象的內(nèi)部結(jié)構描述為一組狀態(tài)S1, S2,...
摘要:所以僅用于簡化理解,快速入門,依然需要閱讀有深入研究的文章來加深對各種異步流程控制的方法的掌握。 原文地址:http://zodiacg.net/2015/08/javascript-async-control-flow/ 隨著ES6標準逐漸成熟,利用Promise和Generator解決回調(diào)地獄問題的話題一直很熱門。但是對解決流程控制/回調(diào)地獄問題的各種工具認識仍然比較麻煩。最近兩天...
閱讀 1075·2023-04-26 02:02
閱讀 2416·2021-09-26 10:11
閱讀 3569·2019-08-30 13:10
閱讀 3758·2019-08-29 17:12
閱讀 732·2019-08-29 14:20
閱讀 2199·2019-08-28 18:19
閱讀 2245·2019-08-26 13:52
閱讀 969·2019-08-26 13:43