成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Promise源碼學(xué)習(xí)(1)

young.li / 3501人閱讀

摘要:工作當(dāng)中經(jīng)常會(huì)用到,在此進(jìn)行深入學(xué)習(xí)異步編程解決方案是異步編程的一種解決方案,比傳統(tǒng)的解決方案回調(diào)函數(shù)和事件更合理和更強(qiáng)大。所有源碼注釋見(jiàn)學(xué)習(xí)筆記

工作當(dāng)中經(jīng)常會(huì)用到Promise,在此進(jìn)行深入學(xué)習(xí)

異步編程解決方案

Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)最早提出和實(shí)現(xiàn),ES6 將其寫(xiě)進(jìn)了語(yǔ)言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了Promise對(duì)象.
關(guān)于其他異步方案,有許多精彩的文章,在此不再詳述。接下來(lái)直接進(jìn)入正題。
項(xiàng)目地址This is a polyfill of the ES6 Promise
項(xiàng)目結(jié)構(gòu)

new Promise()
let p = new Promise(function (resolve, reject) {
  console.log(1);
  resolve(2);
})
p.then(function (val) {
  console.log(val);
})

這是一段簡(jiǎn)單實(shí)例,讓我們跟著源碼一起看發(fā)生了些什么,代碼當(dāng)中加入了個(gè)人理解的注釋。
首先看一下整理后的promise.js

class Promise {
  constructor (resolver) {
    this[PROMISE_ID] = nextId(); //生成id
    this._result = this._state = undefined;
    this._subscribers = [];//訂閱者

    //一般使用時(shí),new時(shí)立即執(zhí)行一次使用者傳入的resolver,印證了一旦promise開(kāi)始執(zhí)行無(wú)法暫停
    if (noop !== resolver) {
      typeof resolver !== "function" && needsResolver();
      this instanceof Promise ? initializePromise(this, resolver) : needsNew();//調(diào)用resolver
    }
  }

  catch (onRejection) {
    return this.then(null, onRejection);
  }

  // finally 相當(dāng)于對(duì)當(dāng)前promise注冊(cè)resolve和reject兩種監(jiān)聽(tīng)
  //如果為 resolve 執(zhí)行一次cb 然后把原來(lái)的value繼續(xù)傳遞
  finally (callback) {
    let promise = this;
    let constructor = promise.constructor;

    return promise.then(value => constructor.resolve(callback()).then(() => value),
      reason => constructor.resolve(callback()).then(() => {
        throw reason;
      }));
  }
}
Promise.prototype.then = then;
export default Promise;
Promise.all = all;
Promise.race = race;
Promise.resolve = Resolve;
Promise.reject = Reject;

細(xì)節(jié)均在注釋當(dāng)中。
這里主要是定義了Promise類(lèi)和定義了一些方法如then,all等等,那么new Promise()主要是初始化了對(duì)象的一些屬性,同時(shí)會(huì)立即執(zhí)行resolver,印證了一旦promise開(kāi)始執(zhí)行無(wú)法暫停。接下來(lái)繼續(xù)我們的思路,看initializePromise方法發(fā)生了什么。

//initializePromise(this, resolver)
function initializePromise (promise, resolver) {
  try {
    //執(zhí)行resolver 傳入回調(diào)
    resolver(function resolvePromise (value) {
      resolve(promise, value);
    }, function rejectPromise (reason) {
      reject(promise, reason);
    });
  } catch (e) {
    reject(promise, e);
  }
}

這里執(zhí)行了我們傳入的function,同時(shí)也給了使用者resolve和reject函數(shù),由于reject相對(duì)簡(jiǎn)單,這里我們先看reject如何實(shí)現(xiàn)。

// 通用的reject方法
function reject (promise, reason) {
  if (promise._state !== PENDING) {
    return;
  }
  promise._state = REJECTED;
  promise._result = reason;

  asap(publishRejection, promise);//as soon as possible
}

reject方法中將promise的對(duì)象的狀態(tài)設(shè)置為rejected,設(shè)置了執(zhí)行的最終結(jié)果值result,隨后再asap(調(diào)度執(zhí)行策略)中執(zhí)行publishRejection,通知通過(guò)then方法注冊(cè)到_subscribers的訂閱者們,我被reject啦??!,來(lái)執(zhí)行回調(diào),下面是publishRejection方法。

function publishRejection (promise) {
  if (promise._onerror) {
    promise._onerror(promise._result);
  }

  publish(promise);
}

//通用的publish
function publish (promise) {
  let subscribers = promise._subscribers;
  let settled = promise._state;

  //沒(méi)有訂閱者
  if (subscribers.length === 0) {
    return;
  }

  let child, callback, detail = promise._result;

  //這里i+=3 是因?yàn)閠hen注冊(cè)時(shí) i是promise,i+1是resolve,i+2是reject
  for (let i = 0; i < subscribers.length; i += 3) {
    child = subscribers[i];
    callback = subscribers[i + settled];

    if (child) {
      invokeCallback(settled, child, callback, detail);//執(zhí)行回調(diào)
    } else {
      callback(detail);
    }
  }
  //通知完畢,清除訂閱
  promise._subscribers.length = 0;
}

上述就是reject之后的大致流程細(xì)節(jié)可以看注釋。
讓我們?cè)倩氐竭@里:

//initializePromise(this, resolver)
function initializePromise (promise, resolver) {
  try {
    //執(zhí)行resolver 傳入回調(diào)
    resolver(function resolvePromise (value) {
      resolve(promise, value);
    }, function rejectPromise (reason) {
      reject(promise, reason);
    });
  } catch (e) {
    reject(promise, e);
  }
}

resolve發(fā)生了什么呢?

// 通用的resolve方法 繼續(xù)傳遞執(zhí)行
function resolve (promise, value) {
  if (promise === value) {//如果resolve原對(duì)象
    reject(promise, selfFulfillment());//設(shè)置rejected狀態(tài)
  } else if (objectOrFunction(value)) {//如果val 是對(duì)象或函數(shù)
    handleMaybeThenable(promise, value, getThen(value));//getThen(value) 獲取val.then方法
  } else {//not obj or not fnc
    fulfill(promise, value);//設(shè)置pending result val
  }
}

resolve一個(gè)值,這里分了三種情況處理
1、如果resolve原對(duì)象,直接reject,拋錯(cuò)。
2、如果是對(duì)象或函數(shù),繼續(xù)處理。
3、如果是簡(jiǎn)單值,//改變promise 狀態(tài)為FULFILLED(完成狀態(tài)) 同時(shí)設(shè)置result,發(fā)起publish

如果非要簡(jiǎn)單理解,resolve就是不斷抽絲剝繭的處理直到給promise一個(gè)確定的完成態(tài)或拒絕態(tài)

fulfill方法比較簡(jiǎn)單,asap下文介紹

//改變promise 狀態(tài)為FULFILLED(完成狀態(tài))  同時(shí)設(shè)置result
function fulfill (promise, value) {
  if (promise._state !== PENDING) {
    return;
  }

  promise._result = value;
  promise._state = FULFILLED;

  if (promise._subscribers.length !== 0) {//通知
    asap(publish, promise);
  }
}

這里重點(diǎn)看handleMaybeThenable方法

/*maybeThenable:value;then:value.then*/
function handleMaybeThenable (promise, maybeThenable, then) {//thenable obj or promise
  /* originalThen : 定義的原始then;originalResolve:原始resolve*/
  if (maybeThenable.constructor === promise.constructor && //判斷是否是promise且沒(méi)經(jīng)修改原生方法
    then === originalThen && maybeThenable.constructor.resolve === originalResolve) {
    handleOwnThenable(promise, maybeThenable);//maybeThenable是一個(gè)原生的promise
  } else {//maybeThenable
    if (then === TRY_CATCH_ERROR) {// getThen 拋錯(cuò)
      reject(promise, TRY_CATCH_ERROR.error);
      TRY_CATCH_ERROR.error = null;//釋放引用
    } else if (then === undefined) {//若不是一個(gè)thenable,直接完成態(tài)
      fulfill(promise, maybeThenable);
    } else if (isFunction(then)) {//若是一個(gè)thenable
      handleForeignThenable(promise, maybeThenable, then);
    } else {//若不是一個(gè)thenable,直接完成態(tài)
      fulfill(promise, maybeThenable);//改變promise 狀態(tài)為完成態(tài)  同時(shí)設(shè)置result
    }
  }
}

這里就對(duì)傳入的值做了詳細(xì)區(qū)分
若是原生Promise對(duì)象:若是fulfilled或rejected,直接發(fā)起publish,如果是pending狀態(tài),調(diào)用then來(lái)注冊(cè)訂閱回調(diào)。
若是thenable:特殊處理handleForeignThenable
"其他fulfill或reject
這里看一下handleForeignThenable方法

/*
 * thenable 是函數(shù),有then方法
 * */
//handleForeignThenable(promise, maybeThenable, then);
function handleForeignThenable (promise, thenable, then) {
  asap(promise => {//asap這里默認(rèn)分析 setTimeout(fn,0) 下一輪任務(wù)開(kāi)始時(shí)執(zhí)行
    var sealed = false;//是否有結(jié)果
    //value:thenable傳入的參數(shù) ,嘗試執(zhí)行
    var error = tryThen(then, thenable, value => {//fullfill時(shí),
      if (sealed) {
        return;
      }
      sealed = true;
      if (thenable !== value) {//如果不是直接resolve原對(duì)象
        resolve(promise, value);//繼續(xù)對(duì)resolve的val進(jìn)行resolve處理
      } else {
        fulfill(promise, value);
      }
    }, reason => {//reject
      if (sealed) {
        return;
      }
      sealed = true;

      reject(promise, reason);
    }, "Settle: " + (promise._label || " unknown promise"));

    if (!sealed && error) {//拋錯(cuò) 未正常執(zhí)行resolve reject
      sealed = true;
      reject(promise, error);
    }
  }, promise);
}

看起來(lái)比較亂,但思路比較簡(jiǎn)單,這里就對(duì)thenable進(jìn)行嘗試執(zhí)行,如果返回結(jié)果正常就繼續(xù)resolve處理直到解析出一個(gè)值,否則拋錯(cuò)等等。

Then

上文說(shuō)到了很多訂閱啦,publish啦,訂閱時(shí)哪里來(lái)的呢,上文只看到了每次執(zhí)行完?duì)顟B(tài)改變的時(shí)候要publish,publish給誰(shuí)呢,then方法會(huì)給出答案

export default function then (onFulfillment, onRejection) {
  const parent = this;

  //新建一個(gè)不執(zhí)行的promise對(duì)象用于返回結(jié)果,可鏈?zhǔn)秸{(diào)用
  const child = new this.constructor(noop);

  if (child[PROMISE_ID] === undefined) {//TODO
    makePromise(child);//初始化基本的promie 屬性
  }
  //promise state
  const {_state} = parent;

  if (_state) {// 如果狀態(tài)已完成或已拒絕,無(wú)需訂閱,直接執(zhí)行回調(diào)返回結(jié)果,印證了一旦promise有了結(jié)果無(wú)法再次改變
    const callback = arguments[_state - 1];
    asap(() => invokeCallback(_state, child, callback, parent._result));
  } else {//訂閱來(lái)注冊(cè)回調(diào)
    subscribe(parent, child, onFulfillment, onRejection);
  }
  //then reuturn的新promise
  return child;
}

這里情況分兩種
已完成或已拒絕:直接執(zhí)行回調(diào)返回結(jié)果,印證了一旦promise有了結(jié)果無(wú)法再次改變
pending未完成:訂閱來(lái)注冊(cè)回調(diào)
這里先看invokeCallback方法。

//asap(() => invokeCallback(_state, child, callback, parent._result));
//執(zhí)行回調(diào):
function invokeCallback (settled, promise, callback, detail) {
  let hasCallback = isFunction(callback),
    value, error, succeeded, failed;

  if (hasCallback) {
    value = tryCatch(callback, detail);//嘗試執(zhí)行使用者then()傳入的回調(diào),成功時(shí)value 是then()注冊(cè)的回調(diào)方法的返回值

    if (value === TRY_CATCH_ERROR) {
      failed = true;
      error = value.error;
      value.error = null;
    } else {
      succeeded = true;
    }

    if (promise === value) {//若return this
      reject(promise, cannotReturnOwn());
      return;
    }

  } else {// then 未傳入相關(guān)回調(diào),繼續(xù)傳遞
    value = detail;
    succeeded = true;
  }

  if (promise._state !== PENDING) {
    // noop
  } else if (hasCallback && succeeded) {
    resolve(promise, value);//value 可能為thenable,繼續(xù)處理,抽絲剝繭
  } else if (failed) {//有cb 且失敗
    reject(promise, error);
  } else if (settled === FULFILLED) {//無(wú)cb
    fulfill(promise, value);
  } else if (settled === REJECTED) {//無(wú)cb
    reject(promise, value);
  }
}

接下來(lái)是重要的subscribe方法。

/*
 * parent:thenable
 * child : undefined or other
 * */
//subscribe(parent, child, onFulfillment, onRejection);
//如果promise仍是pending,則將回調(diào)函數(shù)加入_subscribers等待通知
function subscribe (parent, child, onFulfillment, onRejection) {
  let {_subscribers} = parent;//取注冊(cè)的所有訂閱
  let {length} = _subscribers;

  parent._onerror = null;

  _subscribers[length] = child;//擴(kuò)充訂閱  3個(gè)一循環(huán)
  _subscribers[length + FULFILLED] = onFulfillment;
  _subscribers[length + REJECTED] = onRejection;

  /*
  * 1、如果之前有訂閱且狀態(tài)是pending, 訂閱就好了,等待resolve完成時(shí)的發(fā)布通知執(zhí)行就好
  * 2、如果之前有訂閱且狀態(tài)不是pending,繼續(xù)加入訂閱就好,length=0時(shí)已經(jīng)準(zhǔn)備調(diào)度發(fā)布了,pulish執(zhí)行時(shí)會(huì)清空
  * 3、如果之前無(wú)訂閱且狀態(tài)是pending,訂閱就好了,等待resolve完成時(shí)的發(fā)布通知執(zhí)行就好
  * 4、如下,趕緊調(diào)度執(zhí)行獲取結(jié)果
  * */
  if (length === 0 && parent._state) {//如果之前沒(méi)有訂閱且thenable已不是pending,
    asap(publish, parent);
  }
}

上面就是訂閱的過(guò)程,主要是利用的js單線程的特性,且需要和fuifill和reject執(zhí)行時(shí)發(fā)布publish一起理解.
下面是asap方法

//下一輪事件循環(huán)執(zhí)行
export var asap = function asap (callback, arg) {
  queue[len] = callback;//2個(gè)一組
  queue[len + 1] = arg;
  len += 2;
  if (len === 2) {
    /*
     如果隊(duì)列長(zhǎng)度是2 ,那意味著我們需要調(diào)度一次隊(duì)列flush,
     如果隊(duì)列flush完成前有其他的回調(diào)進(jìn)入隊(duì)列,這些進(jìn)入的回調(diào)會(huì)在當(dāng)前已調(diào)度的flush執(zhí)行
     * */

    // If len is 2, that means that we need to schedule(調(diào)度) an async flush.
    // If additional callbacks are queued before the queue is flushed, they
    // will be processed by this flush that we are scheduling.
    if (customSchedulerFn) {
      customSchedulerFn(flush);
    } else {//一般默認(rèn)
      scheduleFlush();
    }
  }
}

function flush () {
  for (let i = 0; i < len; i += 2) {
    let callback = queue[i];
    let arg = queue[i + 1];

    callback(arg);

    queue[i] = undefined;
    queue[i + 1] = undefined;
  }

  len = 0;//邏輯清空隊(duì)列
}

本文默認(rèn)分析采用的調(diào)度策略時(shí)setTimeout方法,asap里維護(hù)了一個(gè)執(zhí)行隊(duì)列queue。這里涉及到了一些
js異步編程機(jī)制,推薦閱讀從瀏覽器多進(jìn)程到JS單線程,JS運(yùn)行機(jī)制最全面的一次梳理

結(jié)語(yǔ)

本文主要是跟著源碼的思路簡(jiǎn)單過(guò)了一遍代碼,加入了個(gè)人的理解。同時(shí)還有一些如Promise.all等等方法將在下篇一起分析。
閱讀代碼前,也學(xué)習(xí)了阮一峰老師關(guān)于Promise的文章,在此一并感謝。
所有源碼注釋見(jiàn)promise學(xué)習(xí)筆記

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94256.html

相關(guān)文章

  • Promise源碼學(xué)習(xí)(2)

    摘要:源碼學(xué)習(xí)本篇為上一篇源碼學(xué)習(xí)的補(bǔ)充,主要是來(lái)介紹和方法。那個(gè)率先改變的實(shí)例的返回值,就傳遞給的回調(diào)函數(shù)。基本介紹可見(jiàn)阮一峰老師的書(shū)籍。的狀態(tài)由決定,分成兩種情況。只有的狀態(tài)都變成,的狀態(tài)才會(huì)變成,此時(shí)的返回值組成一個(gè)數(shù)組,傳遞給的回調(diào)函數(shù)。 Promise源碼學(xué)習(xí)(2) 本篇為上一篇源碼學(xué)習(xí)(1)的補(bǔ)充,主要是來(lái)介紹Promise.all()和Promise.race()方法。閑話(huà)少敘...

    cfanr 評(píng)論0 收藏0
  • Promise學(xué)習(xí)筆記(四):源碼core.js解析(下)

    摘要:源碼閱讀階段緊接上一篇這次我們開(kāi)始我們最常用到的部分的源碼解析傳入?yún)?shù)為兩個(gè)函數(shù)和判斷調(diào)用者是否為對(duì)象跳轉(zhuǎn)到了一個(gè)叫做的函數(shù)里面新建一個(gè)對(duì)象傳入函數(shù)傳入給和一個(gè)新的對(duì)象返回新的對(duì)象在這里我們先看看在調(diào)用者不是對(duì)象時(shí)到底做了什么比想象的要簡(jiǎn)單 源碼閱讀階段 緊接上一篇,這次我們開(kāi)始Promise我們最常用到的then部分的源碼解析. then() //傳入?yún)?shù)為兩個(gè)函數(shù),onFulfil...

    VincentFF 評(píng)論0 收藏0
  • Promise學(xué)習(xí)筆記(三):源碼core.js解析(上)

    摘要:源碼閱讀階段先理解根本吧想快點(diǎn)理解的話(huà)可以直接跳到下個(gè)標(biāo)題這部分根據(jù)理解將持續(xù)修改空函數(shù)用于判斷傳入構(gòu)造器的函數(shù)是否為空函數(shù)如果為空函數(shù)構(gòu)造一個(gè)對(duì)象并初始化狀態(tài)為終值回調(diào)狀態(tài)和隊(duì)列記錄內(nèi)部最后的一次錯(cuò)誤空對(duì)象標(biāo)識(shí)表示發(fā)生了錯(cuò)誤暴露模塊接口為 源碼閱讀階段 先理解Promise根本吧,想快點(diǎn)理解的話(huà)可以直接跳到下個(gè)標(biāo)題.這部分根據(jù)理解將持續(xù)修改. Promise(fn) function...

    wuyangchun 評(píng)論0 收藏0
  • 從react-start到co源碼(三)

    摘要:第三篇腳手架依賴(lài)的核心庫(kù)的源碼解析。該篇是這個(gè)系列文章的第三篇主要是對(duì)的源碼進(jìn)行分析講解。的源碼十分簡(jiǎn)單但實(shí)現(xiàn)的功能卻是十分的強(qiáng)大。源碼概括源碼主要包含了兩部分公共方法和私有方法。 react作為當(dāng)前十分流行的前端框架,相信很多前端er都有蠢蠢欲動(dòng)的學(xué)習(xí)它的想法。工欲善其事,必先利其器。這篇文章就簡(jiǎn)單的給大家介紹一下如何我快速的搭建一個(gè)react前端開(kāi)發(fā)環(huán)境。主要針對(duì)于react小白,...

    wind5o 評(píng)論0 收藏0
  • Promise學(xué)習(xí)筆記(二):規(guī)范

    摘要:下一篇大概就是源碼方面的學(xué)習(xí)筆記了龜速學(xué)習(xí)中這一次我是去看了下規(guī)范照例傳送門(mén)圖靈社區(qū)規(guī)范首先吧個(gè)人總結(jié)下該用的詞解決結(jié)婚拒絕婉拒終值值傳家寶拒因好人卡等等異常車(chē)禍理下概念我們的的就像是一場(chǎng)姻緣對(duì)吧解決呢就是結(jié)婚成功啦傳家寶也如愿的傳給下一代 下一篇大概就是源碼方面的學(xué)習(xí)筆記了...龜速學(xué)習(xí)中... 這一次我是去看了下Promises/A+規(guī)范照例傳送門(mén):圖靈社區(qū)Promises/A+規(guī)...

    _Suqin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<