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