摘要:最后當的返回值為時,也會執行方法。遍歷傳入的數組,經過的源碼可以看到,如果是一個則戶直接將這個返回,最后數組中的哪個優先回調即執行。
前言
then/promise項目是基于Promises/A+標準實現的Promise庫,從這個項目當中,我們來看Promise的原理是什么,它是如何做到的,從而更加熟悉Promise
分析從index.js當中知道,它是先引出了./core.js,隨后各自執行了其他文件的代碼,通過requeire的方法。
我們首先先想一下最基礎的promise用法
new Promise((resolve, reject) => { resolve(4); }).then(res => { console.log(res); // export 4 });Promise中的標準
標準中規定:
Promise對象初始狀態為 Pending,在被 resolve 或 reject 時,狀態變為 Fulfilled 或 Rejected
resolve接收成功的數據,reject接收失敗或錯誤的數據
Promise對象必須有一個 then 方法,且只接受兩個可函數參數 onFulfilled、onRejected
index.js
"use strict"; module.exports = require("./core.js"); require("./done.js"); require("./finally.js"); require("./es6-extensions.js"); require("./node-extensions.js"); require("./synchronous.js");
我們先看src/core.js
function Promise(fn) { // 判斷 this一定得是object不然就會報錯,這個方法一定得要new出來 if (typeof this !== "object") { throw new TypeError("Promises must be constructed via new"); } // 判斷fn 一定得是一個函數 if (typeof fn !== "function") { throw new TypeError("Promise constructor"s argument is not a function"); } this._deferredState = 0; this._state = 0; this._value = null; this._deferreds = null; if (fn === noop) return; // 最終doResolve很關鍵 doResolve(fn, this); }
Promise是一個構造方法,開始時,它進行了校驗,確保了fn是一個函數,隨后對一些變量進行了初始化,最后執行了doResolve()
我們接著看doResolve這個方法。
/** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. */ // // 確保`onFulfilled`和`onRejected`方法只調用一次 // 不保證異步 function doResolve(fn, promise) { var done = false; var res = tryCallTwo(fn, function (value) { // 如果done 為true 則return if (done) return; done = true; // 回調執行 resolve() resolve(promise, value); }, function (reason) { // 如果done 為true 則return if (done) return; done = true; reject(promise, reason); }); // res為truCallTwo()的返回值 // 如果done沒有完成 并且 res 是 `IS_ERROR`的情況下 // 也會執行reject(),同時讓done完成 if (!done && res === IS_ERROR) { done = true; reject(promise, LAST_ERROR); } }
doResolve最關鍵的是執行了tryCallTwo方法,這個方法的第二,第三個參數都是回調,當執行回調后,done為true,同時各自會執行resolve()或者reject()方法。最后當tryCallTwo的返回值為IS_ERROR時,也會執行reject()方法。
我們先來看一下tryCallTwo方法
function tryCallTwo(fn, a, b) { try { fn(a, b); } catch (ex) { LAST_ERROR = ex; return IS_ERROR; } }
fn實際就是Promise初始化時的匿名函數(resolve, reject) => {},a,b則代表的是resolve()和reject()方法,當我們正常執行完promise函數時,則執行的是resolve則在doResolve中,我們當時執行的第二個參數被回調,如果報錯,reject()被執行,則第二個參數被回調。最后捕獲了異常,當發生了報錯時,會return IS_ERROR,非報錯時會return undinfed
再回到剛才的doResolve方法,當執行了第二個參數的回調之后,會執行resolve方法
function resolve(self, newValue) { // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure // 不能吃傳遞自己 if (newValue === self) { // 報錯 return reject( self, new TypeError("A promise cannot be resolved with itself.") ); } // promise作為參數 if ( newValue && (typeof newValue === "object" || typeof newValue === "function") ) { // 獲取它的promise方法 讀取newValue.then var then = getThen(newValue); if (then === IS_ERROR) { // 如果then IS_ERROR return reject(self, LAST_ERROR); } if ( // 如果then是self的then // 并且Promise then === self.then && // newValue 屬于Promise newValue instanceof Promise ) { // _state為3 // 一般then之后走這里 // 執行then(newValue)返回了promise self._state = 3; // selft.value為newValue self._value = newValue; // 當state為3時執行 finale finale(self); return; } else if (typeof then === "function") { doResolve(then.bind(newValue), self); return; } } self._state = 1; self._value = newValue; finale(self); }
在沒有鏈式調用then的情況下(也就是只要一個then)的情況下,會將內部狀態_state設置成3,將傳入值賦給內部變量_value最后會執行final()方法,不然則會使用doResolve來調用then
我們再來看下reject
function reject(self, newValue) { // _state = 2為reject self._state = 2; self._value = newValue; if (Promise._onReject) { Promise._onReject(self, newValue); } finale(self); }
在reject當中我們的_state變更為了2,同樣最后finale被調用。
我們來看下finale函數
// 執行自己的deferreds function finale(self) { if (self._deferredState === 1) { handle(self, self._deferreds); self._deferreds = null; } if (self._deferredState === 2) { for (var i = 0; i < self._deferreds.length; i++) { // 遍歷handle handle(self, self._deferreds[i]); } // 將deferred 置空 self._deferreds = null; } }
在該方法當中根據不同的_deferredState,會執行不同的handle方法。
我們再來看handle方法
function handle(self, deferred) { while (self._state === 3) { self = self._value; } // 如果有onHandle方法 則執行該方法 if (Promise._onHandle) { Promise._onHandle(self); } // (初始 _state 為0) if (self._state === 0) { // (初始 _deferredState 為0) if (self._deferredState === 0) { self._deferredState = 1; self._deferreds = deferred; return; } // 如果 _deferredState是1 則__deferreds是一個數組 if (self._deferredState === 1) { self._deferredState = 2; self._deferreds = [self._deferreds, deferred]; return; } // 當走到這里 _deferredState應該是2 將deferred // 插入到數組當中 self._deferreds.push(deferred); return; } handleResolved(self, deferred); }
這里比較關鍵的應該就是通過deferredState不同的狀態,將deferred放入deferreds當中。另外當我們的_state不為0時,最終會執行handleResolved。
繼續看handleResolve()方法
function handleResolved(self, deferred) { asap(function() { // _state為1時,cb = onFulfilled 否則 cb = onRejected var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; if (cb === null) { if (self._state === 1) { resolve(deferred.promise, self._value); } else { reject(deferred.promise, self._value); } return; } var ret = tryCallOne(cb, self._value); if (ret === IS_ERROR) { reject(deferred.promise, LAST_ERROR); } else { resolve(deferred.promise, ret); } }); }.then((res) => { }).catch((error) => { })
在這個方法當中,會根據我們任務(_state)的不同狀態,來執行onFulfilled或者onRejected方法。當此方法調用時,也就是我們一個簡單的Promise的結束。
回到剛才說的Promise構造方法結束的時候
設置了Promise函數的一些變量
Promise._onHandle = null; Promise._onReject = null; Promise._noop = noop;
隨后在Promise的原型上設置了then方法。
Promise.prototype.then = function(onFulfilled, onRejected) { // 首先看這是誰構造的 如果不是promise // 則return 執行safeThen if (this.constructor !== Promise) { return safeThen(this, onFulfilled, onRejected); } // 如果是則初始化一個Promise 但是參數 noop 為空對象 {} var res = new Promise(noop); // 隨后執行handle方法 handle(this, new Handler(onFulfilled, onRejected, res)); return res; };
在then這個方法中首先判斷了它是否由Promise構造的,如果不是,則返回并執行safeThen,不然則執行Promise構造一個res對象,然后執行handle方法,最后將promise變量res返回。handle方法之前有提過,在這里,當初始化時_state和_deferred的轉改都為0,因此它會將defrred保存到promise當中。
先看一下上面說的safeThen方法
function safeThen(self, onFulfilled, onRejected) { return new self.constructor(function (resolve, reject) { var res = new Promise(noop); res.then(resolve, reject); handle(self, new Handler(onFulfilled, onRejected, res)); }); }流程
需要有一個Promise的構造方法,這個構造方法最終會執行它的參數(resolve, reject) => {},聲明的then方法會通過handle()方法將onFulfilled和onRejected方法保存起來。當在外部調用resolve或者onRejected時,最終也會執行handle但是它,會最后根據狀態來執行onFulfilled或者onRejected。從而到我們的then回調中。
Promise的擴展 done對done的擴展在src/done.js當中
"use strict"; var Promise = require("./core.js"); module.exports = Promise; Promise.prototype.done = function (onFulfilled, onRejected) { var self = arguments.length ? this.then.apply(this, arguments) : this; self.then(null, function (err) { setTimeout(function () { throw err; }, 0); }); };
內部執行了then()
finally對finally的擴展在src/finally.js當中
在Promise的標準當中,本身是沒有finally方法的,但是在ES2018的標準里有,finally的實現如下
"use strict"; var Promise = require("./core.js"); module.exports = Promise; Promise.prototype.finally = function (callback) { return this.then(function (value) { return Promise.resolve(callback()).then(function () { return value; }); }, function (err) { return Promise.resolve(callback()).then(function () { throw err; }); }); };
Promise的onFulfilled和onRejected 不管回調的哪個,最終都會觸發callback 回調。還要注意的一點是finally的返回也是一個Promise。
es6-extensions.js在es6-extensions.js文件當中包含了ES6的一些擴展。
Promise.resolvefunction valuePromise(value) { var p = new Promise(Promise._noop); // 將_state賦值為 非0 // _value進行保存 p._state = 1; p._value = value; // 這樣做的目的是省略的一些前面的邏輯 return p; } Promise.resolve = function (value) { if (value instanceof Promise) return value; if (value === null) return NULL; if (value === undefined) return UNDEFINED; if (value === true) return TRUE; if (value === false) return FALSE; if (value === 0) return ZERO; if (value === "") return EMPTYSTRING; // value return new Promise if (typeof value === "object" || typeof value === "function") { try { var then = value.then; if (typeof then === "function") { // 返回 返回了一個新的Promise對象 return new Promise(then.bind(value)); } } catch (ex) { // 如果報錯 則返回一個就只 return new Promise(function (resolve, reject) { reject(ex); }); } } return valuePromise(value); };Promise.reject
Promise.reject = function (value) { return new Promise(function (resolve, reject) { reject(value); }); };Promise.all
Promise.all = function (arr) { // 類似深拷貝了一份給了args var args = Array.prototype.slice.call(arr); return new Promise(function (resolve, reject) { // 判斷了all的promise數量 if (args.length === 0) return resolve([]); // remaining則是promise數組的長度 var remaining = args.length; // i為index val 為 promise function res(i, val) { if (val && (typeof val === "object" || typeof val === "function")) { if (val instanceof Promise && val.then === Promise.prototype.then) { while (val._state === 3) { val = val._value; } if (val._state === 1) return res(i, val._value); if (val._state === 2) reject(val._value); // val._state 為 0時 走這里 val.then(function (val) { res(i, val); }, reject); return; } else { var then = val.then; if (typeof then === "function") { var p = new Promise(then.bind(val)); p.then(function (val) { res(i, val); }, reject); return; } } } args[i] = val; // 當所有的promise執行完 則是remaining為0 // 則執行resolve(); if (--remaining === 0) { resolve(args); } } // 遍歷所有的promise for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); };
Promise.all()返回的也是一個Promise函數。
內部有一個remaining變量每當執行完一個promise函數后就會減一,當所有promise執行完,會執行自己的resolve。
Promise.race = function (values) { return new Promise(function (resolve, reject) { values.forEach(function(value){ Promise.resolve(value).then(resolve, reject); }); }); };
遍歷傳入的promise數組,經過Promise.resolve(value)的源碼可以看到,如果value是一個Promise則戶直接將這個value返回,最后數組中的promise哪個優先回調即執行。
Promise.property.catchcatch在標準當中也是沒有,雖然我們用的比較多
Promise.prototype["catch"] = function (onRejected) { return this.then(null, onRejected); };
catch的回調實際是then(null, onRejected)的回調。
廣而告之本文發布于薄荷前端周刊,歡迎Watch & Star ★,轉載請注明出處。
歡迎討論,點個贊再走吧 ????? ~文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100298.html
摘要:返回的結果是一個對象,類似于表示本次后面執行之后返回的結果。對象用于一個異步操作的最終完成或失敗及其結果值的表示簡單點說就是處理異步請求。源碼分析主要脈絡函數調用后,返回一個實例。參考鏈接解釋對象的用法的源碼及其用法 本文始發于我的個人博客,如需轉載請注明出處。為了更好的閱讀體驗,可以直接進去我的個人博客看。 前言 知識儲備 閱讀本文需要對Generator和Promise有一個基本的...
摘要:實例中構造函數的獲取每個實例中都包含一個的屬性,這個屬性指向的是實例的構造函數,在獲取到這個構造函數后,就可以調用它的方法,然后就可以比較了。 焦慮和恐懼的區別是,恐懼是對世界上的存在的恐懼,而焦慮是在我面前的焦慮。——薩特《存在與虛無》 本文為讀 lodash 源碼的第十九篇,后續文章會更新到這個倉庫中,歡迎 star:pocket-lodash gitbook也會同步倉庫的更新,...
摘要:目錄源碼之下無秘密做最好的源碼分析教程源碼分析之番外篇的前生今世的前生今世之一簡介的前生今世之二小結的前生今世之三詳解的前生今世之四詳解源碼分析之零磨刀不誤砍柴工源碼分析環境搭建源碼分析之一揭開神秘的紅蓋頭源碼分析之一揭開神秘的紅蓋頭客戶端 目錄 源碼之下無秘密 ── 做最好的 Netty 源碼分析教程 Netty 源碼分析之 番外篇 Java NIO 的前生今世 Java NI...
摘要:為的項,取出來的分別為和,所以上的和方法,調用的是中的方法,實質是往各自的回調列表中添加回調函數。進度回調函數數組。參數為異步對象的索引值,參數為對應的上下文數組,即或,為對應的回調函數數組,即或。 Deferred 模塊也不是必備的模塊,但是 ajax 模塊中,要用到 promise 風格,必需引入 Deferred 模塊。Deferred 也用到了上一篇文章《讀Zepto源碼之C...
摘要:背景最近發現的回調方法,在連接創建成功和讀取數據后都會被回調。那我也嘗試著從源碼找到答案吧。回調流程分析的回調流程和流程沒有什么區別,可參考上文分析。但是在的方法中會調用這個是讀數據的關鍵讀數據分析讀數據分析 背景 最近發現ChannelOutboundHandlerAdapter的read()回調方法,在連接創建成功和讀取數據后都會被回調。因此就產生了疑問為什么建立連接和讀取數據后r...
摘要:第三篇腳手架依賴的核心庫的源碼解析。該篇是這個系列文章的第三篇主要是對的源碼進行分析講解。的源碼十分簡單但實現的功能卻是十分的強大。源碼概括源碼主要包含了兩部分公共方法和私有方法。 react作為當前十分流行的前端框架,相信很多前端er都有蠢蠢欲動的學習它的想法。工欲善其事,必先利其器。這篇文章就簡單的給大家介紹一下如何我快速的搭建一個react前端開發環境。主要針對于react小白,...
閱讀 2574·2021-10-19 11:41
閱讀 2415·2021-09-01 10:32
閱讀 3377·2019-08-29 15:21
閱讀 1755·2019-08-29 12:20
閱讀 1161·2019-08-29 12:13
閱讀 599·2019-08-26 12:24
閱讀 2520·2019-08-26 10:26
閱讀 827·2019-08-23 18:40