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