1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Promise 庫 lie.js 源碼解讀

        共 9684字,需瀏覽 20分鐘

         ·

        2020-11-01 14:21

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


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

        首先是Promise的核心的構(gòu)造函數(shù)的實(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)部定義了幾個(gè)promise實(shí)例的屬性:

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

        對(duì)于promise,我們一般的用法是:

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

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

        function safelyResolveThenable(self, thenable) {  // Either fulfill, reject or reject with error  // 標(biāo)志位,初始態(tài)的promise僅僅只能被resolve/reject一次  var called = false;  function onError(value) {    if (called) {      return;    }    called = true;    // reject這個(gè)promise    handlers.reject(self, value);  }
        function onSuccess(value) { if (called) { return; } called = true; // resolve這個(gè)promise handlers.resolve(self, value); }
        // 用一個(gè)函數(shù)將resolver執(zhí)行包裹一層 function tryToUnwrap() { // 這個(gè)函數(shù)即由調(diào)用方傳入的 thenable(onSuccess, onError); }
        // 用以捕獲resolver在執(zhí)行過程可能拋出的錯(cuò)誤 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方法中設(shè)定了一個(gè)called標(biāo)志位,這是因?yàn)橐坏┮粋€(gè)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í)行過程中沒有報(bào)錯(cuò),且被resolve的話,那么這個(gè)時(shí)候即調(diào)用的onSuccess方法,這個(gè)方法內(nèi)部調(diào)用了handlers.resolve方法。

        接下來我們看下這個(gè)方法的定義:

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

        再回到我們上面舉的這個(gè)例子:

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

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

        將這個(gè)例子拓展一下:

        const promise = new Promise(resolve => {  resolve(1)})
        promise.then(function onFullfilled (value) { console.log(value)})
        在實(shí)例的then方法上傳入一個(gè)onFullfilled回調(diào)執(zhí)行上面的代碼,最后在控制臺(tái)輸出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)建一個(gè)新的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保存了對(duì)于promise this.queue.push(new QueueItem(promise, onFulfilled, onRejected)); }
        return promise;};
        在then方法內(nèi)部首先創(chuàng)建一個(gè)新的promise,接下來會(huì)根據(jù)這個(gè)promise的狀態(tài)來進(jìn)行不同的處理。
        如果這個(gè)promise已經(jīng)被resolve/reject了(即非pending態(tài)),那么會(huì)直接調(diào)用unwrap()方法來執(zhí)行對(duì)應(yīng)的回調(diào)函數(shù);
        如果這個(gè)promise還是處于pending態(tài),那么需要實(shí)例化一個(gè)QueueItem,并推入到queue隊(duì)列當(dāng)中。
        我們首先分析第一種情況,即調(diào)用then方法的時(shí)候,promise的狀態(tài)已經(jīng)被resolve/reject了,那么根據(jù)對(duì)應(yīng)的state來取對(duì)應(yīng)的回調(diào)函數(shù),并調(diào)用unwrap函數(shù)(后面會(huì)詳細(xì)講解這個(gè)方法)。
        function unwrap(promise, func, value) {  // 異步執(zhí)行這個(gè)func  immediate(function () {    var returnValue;    try {      // 捕獲onFulfilled函數(shù)在執(zhí)行過程中的錯(cuò)誤      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);    }  });}
        在這個(gè)函數(shù)中,使用immediate方法統(tǒng)一的將func方法異步的執(zhí)行。并將這個(gè)func執(zhí)行的返回值傳遞到下一個(gè)promise的處理方法當(dāng)中。
        因此在上面給的例子當(dāng)中,因?yàn)镻romise的狀態(tài)是被同步resolve的,那么接下來立即調(diào)用then方法,并執(zhí)行傳入的onFullfilled方法。
        第二種情況,如果promise還是處于pending態(tài),這個(gè)時(shí)候不是立即執(zhí)行callback,首先實(shí)例化一個(gè)QueueItem,并緩存到這個(gè)promise的queue隊(duì)列當(dāng)中,延遲執(zhí)行這個(gè)queue當(dāng)中保存的回調(diào)函數(shù)。
        function QueueItem(promise, onFulfilled, onRejected) {  // 首先保存這個(gè)promise  this.promise = promise;  // 如果onFulfilled是一個(gè)函數(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是一個(gè)函數(shù),那么就會(huì)覆蓋callFulfilled方法// 如果onFulfilled不是一個(gè)函數(shù),那么就會(huì)直接調(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個(gè)參數(shù):promise,onFullfilled,onRejected。
        promise
        在then當(dāng)中新創(chuàng)建的promise對(duì)象
        onFullfilled:上一個(gè)promise被resolve后需要調(diào)用的回調(diào)
        onRejected:上一個(gè)promise被reject后需要調(diào)用的回調(diào)函數(shù)


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

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

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

        需要注意的是,在每個(gè)then方法內(nèi)部創(chuàng)建的新的promise對(duì)象的state為pending態(tài),outcome為null??梢詫⑸厦媸纠膒romise打印到控制臺(tái),你會(huì)非常清晰的看到整個(gè)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 }

        實(shí)際這個(gè)promise鏈?zhǔn)且粋€(gè)嵌套的結(jié)構(gòu),一旦的最外部的promise的狀態(tài)發(fā)生了改變,那么就會(huì)依次執(zhí)行這個(gè)promise的queue隊(duì)列里保存的queueItem的onFulfilled或者onRejected方法,并這樣一直傳遞下去。
        因此這也是大家經(jīng)常看到的promise鏈一旦開始,就會(huì)一直向下執(zhí)行,沒法在哪個(gè)promise的執(zhí)行過程中中斷。
        不過剛才也提到了關(guān)于在then方法內(nèi)部是創(chuàng)建的一個(gè)新的pending狀態(tài)的promise,這個(gè)promise狀態(tài)的改變完全是由上一個(gè)promise的狀態(tài)決定的,如果上一個(gè)promise是被resolve的,那么這個(gè)promise同樣是被resolve的(前提是在代碼執(zhí)行過程中沒有報(bào)錯(cuò)),并這樣傳遞下去,同樣如果上一個(gè)promise是被rejected的,那么這個(gè)狀態(tài)也會(huì)一直傳遞下去。
        如果有這樣一種情況,在某個(gè)promise封裝的請(qǐng)求中,如果響應(yīng)的錯(cuò)誤碼不符合要求,不希望這個(gè)promise繼續(xù)被resolve下去,同時(shí)想要單獨(dú)的catch住這個(gè)響應(yīng)的話,那么可以在then方法中直接返回一個(gè)被rejected的promise。
        這樣在這個(gè)promise后面的then方法中創(chuàng)建的promise的state都會(huì)被rejected,同時(shí)這些promise所接受的fullfilled方法不再執(zhí)行,如果有傳入onRejected方法的話便會(huì)執(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設(shè)計(jì)還是挺巧妙的,promise事實(shí)上就是一個(gè)狀態(tài)機(jī),不過狀態(tài)值能發(fā)生一次轉(zhuǎn)變,由于then方法內(nèi)部每次都是創(chuàng)建了一個(gè)新的promise,這樣也完成了promise的鏈?zhǔn)秸{(diào)用,同時(shí)then方法中的回調(diào)統(tǒng)一設(shè)計(jì)為異步執(zhí)行也保證了代碼邏輯執(zhí)行順序。

        瀏覽 24
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产一区二区女内射 | 亚洲国产日韩欧美 | 综合网激情天天 | 成人永久免费视频在线观看 | 波多野结衣一品二品免费观看AV | 人妻3P真实偷拍 | 黄频视频在线观看免费 | 羞辱调教扒开跪着让客人玩 | 熟女天天干| 操逼www.|