<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Promise 庫 lie.js 源碼解讀

          共 9684字,需瀏覽 20分鐘

           ·

          2020-11-01 14:21

          來源 | https://github.com/CommanderXL/biu-blog/issues/26


          這篇文章是通過lie.js的源碼一起去了解下如何實現(xiàn)Promise相關(guān)的規(guī)范。

          首先是Promise的核心的構(gòu)造函數(shù)的實現(xiàn)。

          function INTERNAL() {}
          var REJECTED = ['REJECTED'];var FULFILLED = ['FULFILLED'];var PENDING = ['PENDING'];
          var handlers = {}
          function Promise (resolver) { if (typeof resolver !== 'function') { throw new TypeError('resolver must be a function'); } this.state = PENDING; this.queue = []; this.outcome = void 0; /* istanbul ignore else */ if (!process.browser) { this.handled = UNHANDLED; } if (resolver !== INTERNAL) { safelyResolveThenable(this, resolver); }}

          構(gòu)造函數(shù)內(nèi)部定義了幾個promise實例的屬性:

          state:promise的狀態(tài)值.有3種:rejected,fulfilled,pending
          queue:queue數(shù)組用以保存這個promise被resolve/rejected后需要異步執(zhí)行的回調(diào)
          outcome:這個promise實例的值

          對于promise,我們一般的用法是:

          // 構(gòu)造函數(shù)當中接收2個參數(shù),resolve/reject,需要注意的是這2個參數(shù)是promise內(nèi)部定義的,用以改變這個promise的狀態(tài)和值const promise = new Promise((resolve, reject) => {  // 同步或者異步的去resolve一個值  resolve(1)})

          給這個Promise構(gòu)造函數(shù)內(nèi)部傳入的resolver由內(nèi)部的方法safelyResolveThenable去執(zhí)行:

          function safelyResolveThenable(self, thenable) {  // Either fulfill, reject or reject with error  // 標志位,初始態(tài)的promise僅僅只能被resolve/reject一次  var called = false;  function onError(value) {    if (called) {      return;    }    called = true;    // reject這個promise    handlers.reject(self, value);  }
          function onSuccess(value) { if (called) { return; } called = true; // resolve這個promise handlers.resolve(self, value); }
          // 用一個函數(shù)將resolver執(zhí)行包裹一層 function tryToUnwrap() { // 這個函數(shù)即由調(diào)用方傳入的 thenable(onSuccess, onError); }
          // 用以捕獲resolver在執(zhí)行過程可能拋出的錯誤 var result = tryCatch(tryToUnwrap); if (result.status === 'error') { onError(result.value); }}
          function tryCatch(func, value) { var out = {}; try { out.value = func(value); out.status = 'success'; } catch (e) { out.status = 'error'; out.value = e; } return out;}

          在safelyResolveThenable方法中設定了一個called標志位,這是因為一旦一個promise的狀態(tài)發(fā)生了改變,那么之后的狀態(tài)不能再次被改變,舉例:

          new Promise((resolve, reject) => {  // 一旦狀態(tài)發(fā)生改變,后面的reject/resolve方法不能起作用  resolve(1)  reject(new Error('error'))  resolve(2)})

          如果給Promise構(gòu)造函數(shù)傳入callback在執(zhí)行過程中沒有報錯,且被resolve的話,那么這個時候即調(diào)用的onSuccess方法,這個方法內(nèi)部調(diào)用了handlers.resolve方法。

          接下來我們看下這個方法的定義:

          handlers.resolve = function (self, value) {  var result = tryCatch(getThen, value);  if (result.status === 'error') {    return handlers.reject(self, result.value);  }  // 判斷這個value是否是個thenable對象  var thenable = result.value;
          if (thenable) { safelyResolveThenable(self, thenable); } else { // 將這個promise的state從 pending -> fulfilled self.state = FULFILLED; // 更改這個promise對應的值 self.outcome = value; var i = -1; var len = self.queue.length; // 依次執(zhí)行這個promise的queue隊列里面每一項queueItem的callFulfilled方法 while (++i < len) { self.queue[i].callFulfilled(value); } } // 返回這個promise對象 return self;}

          再回到我們上面舉的這個例子:

          const promise = new Promise(resolve => {  resolve(1)})

          在這個例子當中,是同步去resolve這個promise,那么返回的這個promise實例的狀態(tài)便為fulfilled,同時outcome的值也被設為1。

          將這個例子拓展一下:

          const promise = new Promise(resolve => {  resolve(1)})
          promise.then(function onFullfilled (value) { console.log(value)})
          在實例的then方法上傳入一個onFullfilled回調(diào)執(zhí)行上面的代碼,最后在控制臺輸出1。
          接下來我們看下Promise原型上then方法的定義:
          Promise.prototype.then = function (onFulfilled, onRejected) {  if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||    typeof onRejected !== 'function' && this.state === REJECTED) {    return this;  }  // 創(chuàng)建一個新的promise  var promise = new this.constructor(INTERNAL);  /* istanbul ignore else */  if (!process.browser) {    if (this.handled === UNHANDLED) {      this.handled = null;    }  }
          // new Promise在內(nèi)部resolve過程中如果是同步的 if (this.state !== PENDING) { var resolver = this.state === FULFILLED ? onFulfilled : onRejected; unwrap(promise, resolver, this.outcome); } else { // 異步的resolve // this.queue保存了對于promise this.queue.push(new QueueItem(promise, onFulfilled, onRejected)); }
          return promise;};
          在then方法內(nèi)部首先創(chuàng)建一個新的promise,接下來會根據(jù)這個promise的狀態(tài)來進行不同的處理。
          如果這個promise已經(jīng)被resolve/reject了(即非pending態(tài)),那么會直接調(diào)用unwrap()方法來執(zhí)行對應的回調(diào)函數(shù);
          如果這個promise還是處于pending態(tài),那么需要實例化一個QueueItem,并推入到queue隊列當中。
          我們首先分析第一種情況,即調(diào)用then方法的時候,promise的狀態(tài)已經(jīng)被resolve/reject了,那么根據(jù)對應的state來取對應的回調(diào)函數(shù),并調(diào)用unwrap函數(shù)(后面會詳細講解這個方法)。
          function unwrap(promise, func, value) {  // 異步執(zhí)行這個func  immediate(function () {    var returnValue;    try {      // 捕獲onFulfilled函數(shù)在執(zhí)行過程中的錯誤      returnValue = func(value);    } catch (e) {      return handlers.reject(promise, e);    }    // 不能返回自身promise    if (returnValue === promise) {      handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));    } else {      handlers.resolve(promise, returnValue);    }  });}
          在這個函數(shù)中,使用immediate方法統(tǒng)一的將func方法異步的執(zhí)行。并將這個func執(zhí)行的返回值傳遞到下一個promise的處理方法當中。
          因此在上面給的例子當中,因為Promise的狀態(tài)是被同步resolve的,那么接下來立即調(diào)用then方法,并執(zhí)行傳入的onFullfilled方法。
          第二種情況,如果promise還是處于pending態(tài),這個時候不是立即執(zhí)行callback,首先實例化一個QueueItem,并緩存到這個promise的queue隊列當中,延遲執(zhí)行這個queue當中保存的回調(diào)函數(shù)。
          function QueueItem(promise, onFulfilled, onRejected) {  // 首先保存這個promise  this.promise = promise;  // 如果onFulfilled是一個函數(shù)  if (typeof onFulfilled === 'function') {    this.onFulfilled = onFulfilled;    // 那么重新賦值callFulfilled函數(shù)    this.callFulfilled = this.otherCallFulfilled;  }  if (typeof onRejected === 'function') {    this.onRejected = onRejected;    this.callRejected = this.otherCallRejected;  }}// 如果onFulfilled是一個函數(shù),那么就會覆蓋callFulfilled方法// 如果onFulfilled不是一個函數(shù),那么就會直接調(diào)用handlers.resolve去遞歸處理promiseQueueItem.prototype.callFulfilled = function (value) {  handlers.resolve(this.promise, value);};QueueItem.prototype.otherCallFulfilled = function (value) {  unwrap(this.promise, this.onFulfilled, value);};QueueItem.prototype.callRejected = function (value) {  handlers.reject(this.promise, value);};QueueItem.prototype.otherCallRejected = function (value) {  unwrap(this.promise, this.onRejected, value);};
          QueueItem構(gòu)造函數(shù)接受3個參數(shù):promise,onFullfilled,onRejected。
          promise
          在then當中新創(chuàng)建的promise對象
          onFullfilled:上一個promise被resolve后需要調(diào)用的回調(diào)
          onRejected:上一個promise被reject后需要調(diào)用的回調(diào)函數(shù)


          接下來我們看下第二種情況是在什么樣的情況下去執(zhí)行的:

          const promise = new Promise(resolve => {  setTimeout(() => {    resolve(1)  }, 3000)})
          promise.then(data => console.log(data))
          在這個例子當中,當過了3s后在控制臺輸出1。在這個例子當中,因為promise內(nèi)部是異步去resolve這個promise。
          在這個promise被resolve前,promise實例通過then方法向這個promise的queue隊列中添加onFullfilled方法,這個queue中保存的方法會等到promise被resolve后才會被執(zhí)行。
          當在實際的調(diào)用resolve(1)時,即promise這個時候才被resolve,那么便會調(diào)用handlers.resolve方法,并依次調(diào)用這個promise的queue隊列當中保存的onFullfilled函數(shù)
          可以看到在QueueItem函數(shù)內(nèi)部,會對onFullfilled和onRejected的參數(shù)類型做判斷,只有當它們是函數(shù)的時候,才會將這個方法進行一次緩存,同時使用otherCallFulfilled方法覆蓋原有的callFulfilled方法。這也是大家經(jīng)常會遇到的值穿透的問題,舉個例子:
          const promise = new Promise(resolve => {  setTimeout(() => {    resolve(2)  }, 2000)})
          promise.then(3).then(console.log)

          最后在控制臺打印出2,而非3。當上一個promise被resolve后,調(diào)用這個promise的queue當中緩存的queueItem上的callFulfilled方法,因為then方法接收的是數(shù)值類型,因此這個queueItem上的callFulfilled方法未被覆蓋,因此這時所做的便是直接將這個queueItem中保存的promise進行resolve,同時將上一個promise的值傳下去。
          可以這樣理解,如果then方法第一個參數(shù)接收到的是個函數(shù),那么就由這個函數(shù)處理上一個promise傳遞過來的值,如果不是函數(shù),那么就像管道一樣,先流過這個then方法,而將上一個值傳遞給下下個then方法接收到的函數(shù)去處理。
          上面提到了關(guān)于unwrap這個函數(shù),這個函數(shù)的作用就是統(tǒng)一的將then方法中接收到的onFullfilled參數(shù)異步的執(zhí)行。
          主要是使用了immediate這個庫。這里說明下為什么統(tǒng)一的要將onFullfilled方法進行異步話的處理呢。
          首先,是要解決代碼邏輯執(zhí)行順序的問題,首先來看一種情況:
          const promise = new Promise(resolve => {  // 情況一:同步resolve  resolve(1)  // 情況二:異步resolve  setTimeout(() => {    resolve(2)  }, 1000)})promise.then(function onFullfilled() {  // do something  foo()})bar()
          這個promise可能會被同步的resolve,也有可能異步的resolve,這個時候如果onFullfilled方法設計成同步的執(zhí)行的話,那么foo及bar的執(zhí)行順序便依賴promise是被同步or異步被resolve,但是如果統(tǒng)一將onFullfilled方法設計成異步的執(zhí)行的話,那么bar方法始終在foo方法前執(zhí)行,這樣就保證了代碼執(zhí)行的順序。
          其次,是要解決同步回調(diào)stackoverflow的問題,具體的鏈接請戳我
          我們看到lie.js的內(nèi)部實現(xiàn)當中,每次在調(diào)用then方法的時候,內(nèi)部都新創(chuàng)建了一個promise的對象并返回,這樣也完成了promise的鏈式調(diào)用。即:
          const Promise = require('lie')const promise = new Promise(resolve => setTimeout(resolve, 3000))promise.then(() => 'a').then(() => 'b').then(() => {})

          需要注意的是,在每個then方法內(nèi)部創(chuàng)建的新的promise對象的state為pending態(tài),outcome為null??梢詫⑸厦媸纠膒romise打印到控制臺,你會非常清晰的看到整個promise鏈的結(jié)構(gòu):

          Promise {  state: [ 'PENDING' ],  queue:   [ QueueItem {       promise: {         state: ['PENDING'],         queue: [           QueueItem {             promise: {               state: ['PENDING'],               queue: [                 QueueItem {                   promise: {                     state: ['PENDING'],                     queue: [],                     outcome: undefined                   }                 }               ],               outcome: undefined,               handled: null             },             onFulfilled: [Function],             callFulfilled: [Function]           }         ],        outcome: undefined,        handled: null               },       onFulfilled: [Function],       callFulfilled: [Function] } ],  outcome: undefined,  handled: null }

          實際這個promise鏈是一個嵌套的結(jié)構(gòu),一旦的最外部的promise的狀態(tài)發(fā)生了改變,那么就會依次執(zhí)行這個promise的queue隊列里保存的queueItem的onFulfilled或者onRejected方法,并這樣一直傳遞下去。
          因此這也是大家經(jīng)??吹降膒romise鏈一旦開始,就會一直向下執(zhí)行,沒法在哪個promise的執(zhí)行過程中中斷。
          不過剛才也提到了關(guān)于在then方法內(nèi)部是創(chuàng)建的一個新的pending狀態(tài)的promise,這個promise狀態(tài)的改變完全是由上一個promise的狀態(tài)決定的,如果上一個promise是被resolve的,那么這個promise同樣是被resolve的(前提是在代碼執(zhí)行過程中沒有報錯),并這樣傳遞下去,同樣如果上一個promise是被rejected的,那么這個狀態(tài)也會一直傳遞下去。
          如果有這樣一種情況,在某個promise封裝的請求中,如果響應的錯誤碼不符合要求,不希望這個promise繼續(xù)被resolve下去,同時想要單獨的catch住這個響應的話,那么可以在then方法中直接返回一個被rejected的promise。
          這樣在這個promise后面的then方法中創(chuàng)建的promise的state都會被rejected,同時這些promise所接受的fullfilled方法不再執(zhí)行,如果有傳入onRejected方法的話便會執(zhí)行onRejected方法,最后一直傳遞到的catch方法中添加的onReject方法。
          someRequest().then(res => {  if (res.error === 0) {    // do something    return res  } else {    return Promise.reject(res)  }}).then(val => {  // do something}).catch(err => {  // do something})
          看完lie的源碼后,覺得promise設計還是挺巧妙的,promise事實上就是一個狀態(tài)機,不過狀態(tài)值能發(fā)生一次轉(zhuǎn)變,由于then方法內(nèi)部每次都是創(chuàng)建了一個新的promise,這樣也完成了promise的鏈式調(diào)用,同時then方法中的回調(diào)統(tǒng)一設計為異步執(zhí)行也保證了代碼邏輯執(zhí)行順序。

          瀏覽 24
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人免费黄色网址 | 一级黄色小视频 | 国产内射网站 | 久久久91精品国产一区陈可心 | 黄色五月婷婷五月 |