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源碼指南

        共 27845字,需瀏覽 56分鐘

         ·

        2021-05-01 10:37

        前言

        什么是Promise?

        相信許多從事前端的小伙伴們都用過Promise,為了解決異步編程的弊端(地獄回調(diào)等問題),ES6提供了一個強大的東西,那就是Promise。Promise是將異步任務轉(zhuǎn)換為同步任務的一個構(gòu)造函數(shù),通過resolve,reject改變?nèi)蝿盏臓顟B(tài),必不可少的then方法用來收Promise的值,這些都是Promise的基本使用。那么Promise是如何處理狀態(tài)的,又是如何實現(xiàn)resove,reject方法的,又是如何實現(xiàn)鏈式調(diào)用的呢,如果你不知道,那么這篇文章可以幫到你,下面我們一起來解析一下Promise到底是如何實現(xiàn)的,相信看完這篇文章,每個人都可以寫出屬于自己的Promise方法。

        這里引入github上的一份符合Promise A+規(guī)范的源碼 https://github.com/then/promise

        函數(shù)對象Promise

        我們先來看看src/index.js這個文件

        一些必要的定義

        // 定義空函數(shù)
        function noop({}

        // 用來存儲錯誤信息
        var IS_ERROR = {}

        // 獲取實例的then
        function getThen(obj{
          try {
            return obj.then;
          } catch (ex) {
            LAST_ERROR = ex;
            return IS_ERROR;
          }
        }

        // 執(zhí)行then方法回調(diào)函數(shù)
        function tryCallOne(fn, a{
          try {
            return fn(a);
          } catch (ex) {
            LAST_ERROR = ex;
            return IS_ERROR;
          }
        }

        // 執(zhí)行Promise構(gòu)造函數(shù)
        function tryCallTwo(fn, a, b{
          try {
            fn(a, b);
          } catch (ex) {
            LAST_ERROR = ex;
            return IS_ERROR;
          }
        }

        Promise構(gòu)造函數(shù)

        function Promise(fn{
          // 校驗實例
          if (typeof this !== 'object') {
            throw new TypeError('Promises must be constructed via new');
          }
          // 校驗Promise的構(gòu)造函數(shù)
          if (typeof fn !== 'function') {
            throw new TypeError('Promise constructor\'s argument is not a function');
          }
          // 存儲的實例狀態(tài) 0代表還未存儲 1代表存儲了1個 2代表存儲了2個
          this._deferredState = 0;
          // Promise的狀態(tài)  0代表padding  1代表Fulfilled  2代表Rejected 3代表resolve傳入的是promise實例
          this._state = 0;
          // Fulfilled的值
          this._value = null;
          // 用來存儲調(diào)用then后的實例
          this._deferreds = null;
          if (fn === noop) return;
          // 處理Promise的參數(shù)
          doResolve(fn, this);
        }
        //以下先不做解釋
        newPromise._onHandle = null;
        newPromise._onReject = null;
        newPromise._noop = noop;

        當使用new操作符實例化Promise的時候,必須傳入Promise的構(gòu)造函數(shù)fn,否則將拋出錯誤
        然后初始化實例的狀態(tài)和值 最后調(diào)用doResolve方法
        下面我們一起來看一下doResolve這個方法

        doResolve方法

        function doResolve(fn, promise{
          // done防止重復觸發(fā)
          var done = false;
          // tryCallTwo  用于處理并掛載reolve,reject方法
          // 傳入3個參數(shù),Promise構(gòu)造函數(shù)本身,resolve回調(diào),reject回調(diào)
          var res = tryCallTwo(fn, function (value{
            if (done) return;
            done = true;
            // 處理resolve方法
            resolve(promise, value);
          }, function (reason{
            if (done) return;
            done = true;
            // 處理reject方法
            reject(promise, reason);
          });
          // 報錯則直接reject
          if (!done && res === IS_ERROR) {
            done = true;
            reject(promise, LAST_ERROR);
          }
        }

        doResolve方法接收兩個參數(shù)(Promise的構(gòu)造函數(shù),Promise的實例也就是this)
        這里的tryCallTwo扮演了一個重要角色,它執(zhí)行了構(gòu)造函數(shù)fn,傳入三個參數(shù)(fn構(gòu)造函數(shù),resolve回調(diào)函數(shù),reject回調(diào)函數(shù))
        我們回過頭來看看tryCallTwo這個函數(shù)

        // 執(zhí)行Promise構(gòu)造函數(shù)
        function tryCallTwo(fn, a, b{
            //...
            fn(a, b);
            //...
        }

        這里的a,b就是Promise的resolvereject
        執(zhí)行fn并傳入ab
        然后resolve和reject又分別執(zhí)行了resolve方法和reject方法
        當實例調(diào)用了resolve之后就會執(zhí)行resolve方法,下面來看看resove這個方法

        resolve方法

        function resolve(self, newValue{
          // 防止resolve的值傳入實例本身
          if (newValue === self) {
            return reject(
              self,
              new TypeError('A promise cannot be resolved with itself.')
            );
          }
          // 這里的if主要用于處理resolve一個Promise
          if (
            newValue &&
            (typeof newValue === 'object' || typeof newValue === 'function')
          ) {
            // 獲取Promise的then方法
            var then = getThen(newValue);
            if (then === IS_ERROR) {
              return reject(self, LAST_ERROR);
            }
            // 如果傳入的是Promise的實例
            if (
              then === self.then &&
              newValue instanceof newPromise
            ) {
              self._state = 3;
              // 直接把傳入的Promise實例掛載到value
              self._value = newValue;
              finale(self);
              return;
            } else if (typeof then === 'function') {  //如果傳入帶有then方法
              // 把then當作構(gòu)造函數(shù)并且把this指向這個then的對象
              doResolve(then.bind(newValue), self);
              return;
            }
          }
          self._state = 1;
          self._value = newValue;
          finale(self);
        }

        resolve方法一開始會先判斷resolve傳入的值,如果resolve傳入的是一個「Promise實例」,將會進行以下處理
        將實例的狀態(tài)變?yōu)?
        將實例的值變?yōu)閭魅氲腜romise實例
        調(diào)用finale方法
        如果resolve傳入的是Promise實例并且「包含then方法」則調(diào)用doResolve執(zhí)行這個實例的構(gòu)造函數(shù)

        如果resolve傳入的是普通值而不是Promise實例,則做以下處理
        將實例的狀態(tài)變?yōu)?
        將實例的值變?yōu)閞esolve傳入的值
        調(diào)用finale方法

        reject方法

        function reject(self, newValue{
          // reject的時候狀態(tài)變?yōu)?
          self._state = 2;
          // 保存錯誤信息到_value
          self._value = newValue;
          if (newPromise._onReject) {
            newPromise._onReject(self, newValue);
          }
          finale(self);
        }

        當Promise執(zhí)行reject的時候此時狀態(tài)變?yōu)?code style="margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">2
        保存reject的錯誤信息到_value調(diào)用finale方法

        finale方法

        function finale(self{
          // 只調(diào)用一次then
          if (self._deferredState === 1) {
            handle(self, self._deferreds);
            self._deferreds = null;
          }
          // 調(diào)用多次then
          if (self._deferredState === 2) {
            // console.log(self._deferreds);
            for (var i = 0; i < self._deferreds.length; i++) {
              handle(self, self._deferreds[i]);
            }
            self._deferreds = null;
          }
        }

        finaale方法在這里的作用就相當于handle方法的中轉(zhuǎn)站,根據(jù)不同的情況去調(diào)用handle方法
        上面有提到過_deferredState是用來記錄存儲的實例狀態(tài)
        「同一個Promise對象下」,_deferredState的值是隨著then調(diào)用的次數(shù)決定的,為什么說只有在同一個Promise對象下才會觸發(fā)呢,我們來看下面一個小例子

        const promise2 = new Promise((resolve,reject) => {
            setTimeout(() => {
              resolve('哈哈哈')
            }, 500);
          })
          .then(res => {}) //第一次then
          .then(res => {}) //第二次then

        到這里很多小伙伴會以為代碼會在’if(self._deferredState === 2){}‘這個判斷里面執(zhí)行
        事實上這樣去調(diào)用then的情況下,_deferredState的值永遠只會=1,只會在’if(self._deferredState === 1){}‘的這個判斷里面去執(zhí)行
        到這里小伙伴們又會說,不對啊,我這個不是在同一個Promise對象下嗎,我不是只實例化了一次嗎?
        這里在使用Promise的時候確實是只實例化了一次,但是每次調(diào)用then方法返回的Promise跟實例的Promise并不是同一個引用,也就是說,這里的self并不是實例出來的對象,后面會詳細介紹then是怎么返回Promise對象的

        promise2.then(res => {
          console.log(res);
        })
        promise2.then(res => {
          console.log(res);
        })

        只有這樣調(diào)用then,才會執(zhí)行’if(self._deferredState === 2){}‘這個判斷

        Promise.prototype.then

        Promise.prototype.then = function(onFulfilled, onRejected{
          if (this.constructor !== Promise) {
            return safeThen(this, onFulfilled, onRejected);
          }
          // 新建一個promise實例
          var res = new Promise(noop);
          // // new Handler 構(gòu)建了一個包含新的promise實例和resolve,reject方法的對象
          handle(thisnew Handler(onFulfilled, onRejected, res));
          // 每次then處理完之后返回一個新的promise實例
          return res;
        };

        接收兩個參數(shù)resolvereject函數(shù)
        一開始先判斷實例的構(gòu)造函數(shù)是不是Promise(防止外部修改prototype.constructor)
        這里的safeThen函數(shù)不作過多解釋,主要用于實例化外部實例的構(gòu)造函數(shù)并返回實例
        創(chuàng)建一個空的Promise實例給res
        我們看看下面這句代碼主要做了什么

        handle(this, new Handler(onFulfilled, onRejected, res));

        我們拆開來看,先看看new Handler實例化得到了什么

        // 這個函數(shù)的作用就是每次then的時候通過這個構(gòu)造函數(shù)根據(jù)resolve和reject構(gòu)建一個新的對象
        function Handler(onFulfilled, onRejected, promise){
          this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
          this.onRejected = typeof onRejected === 'function' ? onRejected : null;
          this.promise = promise;
        }

        由此可見通過實例化Handler函數(shù)可以得到一個大概長這樣子的對象

        所以handle函數(shù)傳入了2個參數(shù),一個是this,一個是像這樣子的對象,那么,接下來就可以看handle函數(shù)做了什么事情了

        function handle(self, deferred{
          // resolve傳入的是promise的實例,this(上下文)則改成傳入的promise實例
          while (self._state === 3) {
            self = self._value;
          }
          if (newPromise._onHandle) {
            newPromise._onHandle(self);
          }
          // 當padding狀態(tài)時(沒有調(diào)用resolve也沒有調(diào)用reject)
          // 存儲新的promise實例
          if (self._state === 0) {
            if (self._deferredState === 0) {
              self._deferredState = 1;
              self._deferreds = deferred;
              return;
            }
            // 已經(jīng)調(diào)用過1次then
            if (self._deferredState === 1) {
              self._deferredState = 2;
              self._deferreds = [self._deferreds, deferred];
              return;
            }
            // 多次then
            self._deferreds.push(deferred);
            return;
          }
          handleResolved(self, deferred);
        }

        看上面的代碼注釋大家都應該知道了,handle函數(shù)主要就是用作修改_deferredState的值和保存每次then生成的新實例

        最后return res 每次調(diào)用then方法都返回一個新的Promise實例

        鏈式調(diào)用

        細心的你可能已經(jīng)發(fā)現(xiàn)了在前面的handle方法里的最下面調(diào)用了一個名為handleResolved的函數(shù),話不多說直接上代碼

        function handleResolved(self, deferred{
          // asap(function() {
          //   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);
          //   }
          // });

          // resolve后,獲取then的回調(diào)
          var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
          // 如果then沒有回調(diào),則手動調(diào)用回調(diào)
          if (cb === null) {
            if (self._state === 1) {
              resolve(deferred.promise, self._value);
            } else {
              reject(deferred.promise, self._value);
            }
            return;
          }
          // 獲取then的返回值
          var ret = tryCallOne(cb, self._value);
          if (ret === IS_ERROR) {
            reject(deferred.promise, LAST_ERROR);
          } else {
            resolve(deferred.promise, ret);
          }
        }

        其實源碼還引入了asap這個庫,我先把這個asap函數(shù)注釋掉了,理由是懶得用npm,本文全程是html+js+live-server
        但是大家千萬不要忽略asap這個函數(shù)!!!
        縱觀全文到現(xiàn)在,大家好像并沒有發(fā)現(xiàn)源碼有一點點異步的信息,大家都知道Promise是異步執(zhí)行的,就是靠asap函數(shù),通過setImmediate這個核心方法去異步執(zhí)行asap里面的東西,有興趣的可以去翻翻asap的源碼看下具體是怎么實現(xiàn)的
        這里只是為了更好的解析源碼,沒有asap那么Promise就沒有意義了,ok我們回歸正文

        這里就很好理解了
        通過tryCallOne函數(shù)得到then的返回值
        然后再次調(diào)用resolve,如果報錯或者手動調(diào)用reject則調(diào)用reject,這樣就完成了Promise的鏈式調(diào)用

        擴展

        src/es6-extensions.js

        定義

        var TRUE = valuePromise(true);
        var FALSE = valuePromise(false);
        var NULL = valuePromise(null);
        var UNDEFINED = valuePromise(undefined);
        var ZERO = valuePromise(0);
        var EMPTYSTRING = valuePromise('');

        function valuePromise(value{
          var p = new newPromise(newPromise._noop);
          p._state = 1;
          p._value = value;
          return p;
        }

        Promise.resolve

        Promise.resolve = function (value{
          // 判斷value是否是Promise得實例
          if (value instanceof Promisereturn value;

          // 因為0,'',null等會被隱式轉(zhuǎn)換成false,所以這里做了精確判斷
          if (value === nullreturn NULL;
          if (value === undefinedreturn UNDEFINED;
          if (value === truereturn TRUE;
          if (value === falsereturn FALSE;
          if (value === 0return ZERO;
          if (value === ''return EMPTYSTRING;


          // 這里的判斷跟之前resolve方法一樣都是判斷傳入的是否是一個Promise
          if (typeof value === 'object' || typeof value === 'function') {
            try {
              var then = value.then;
              if (typeof then === 'function') {
                return new Promise(then.bind(value));
              }
            } catch (ex) {
              return new Promise(function (resolve, reject{
                reject(ex);
              });
            }
          }
          // 根據(jù)valuePromise方法返回一個新的Promise
          return valuePromise(value);
        };

        try/catch 用于捕獲Promise.resolve傳入的val是否包含then方法

        Promise.all

        Promise.all = function (arr{
          var args = iterableToArray(arr);

          return new Promise(function (resolve, reject{
            if (args.length === 0return resolve([]);
            var remaining = args.length;
            function res(i, val{
              if (val && (typeof val === 'object' || typeof val === 'function')) {
                // 如果val是Promise的實例
                if (val instanceof Promise && val.then === Promise.prototype.then) {
                  // _state等于3  證明val實例的值也是一個Promise實例,把val替換成新的Promise實例
                  while (val._state === 3) {
                    val = val._value;
                  }
                  // resolved成功調(diào)用,遞歸處理resolved的值
                  if (val._state === 1return res(i, val._value);
                  if (val._state === 2) reject(val._value);
                  // 處于padding狀態(tài)時調(diào)用then方法并手動處理值
                  val.then(function (val{
                    res(i, val);
                  }, reject);
                  return;
                } else {
                  // 如果不是promise的實例且包含then方法
                  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.all里面全部為fulFilled狀態(tài)后
              if (--remaining === 0) {
                resolve(args);
              }
            }
            for (var i = 0; i < args.length; i++) {
              res(i, args[i]);
            }
          });
        };

        第一行用到了iterableToArray函數(shù),這個函數(shù)的主要作用是把類數(shù)組轉(zhuǎn)換成可被遍歷的數(shù)組

        const p1 = new Promise(() => {})
        const p2 = new Promise(() => {})
        console.log(Array.isArray(Promise.all[p1,p2]))  //false
        // 兼容Array.form這個es6語法
        var iterableToArray = function (iterable{
          if (typeof Array.from === 'function') {
            // ES2015+, iterables exist
            iterableToArray = Array.from;
            return Array.from(iterable);
          }

          // ES5, only arrays and array-likes exist
          iterableToArray = function (xreturn Array.prototype.slice.call(x); };
          return Array.prototype.slice.call(iterable);
        }

        使用Array.prototype.slice.call(x)對Array.from做了兼容

        Promise.all = function (arr{
          var args = iterableToArray(arr);

          return new Promise(function (resolve, reject{
            if (args.length === 0return resolve([]);
            var remaining = args.length;
            function res(i, val{
              ...
            }
            for (var i = 0; i < args.length; i++) {
              res(i, args[i]);
            }
          });
        };

        先不要看res方法,先看看怎么處理傳進來的參數(shù)
        循環(huán)調(diào)用res處理傳進來的每一項,第一個參數(shù)為下標,第二個參數(shù)是數(shù)組的每一項
        再來看下res方法

        function res(i, val{
              if (val && (typeof val === 'object' || typeof val === 'function')) {
                // 如果val是Promise的實例
                if (val instanceof Promise && val.then === Promise.prototype.then) {
                  // _state等于3  證明val實例的值也是一個Promise實例,把val替換成新的Promise實例
                  while (val._state === 3) {
                    val = val._value;
                  }
                  // resolved成功調(diào)用,遞歸處理resolved的值
                  if (val._state === 1return res(i, val._value);
                  if (val._state === 2) reject(val._value);
                  // 處于padding狀態(tài)時調(diào)用then方法并手動處理值
                  val.then(function (val{
                    res(i, val);
                  }, reject);
                  return;
                } else {
                  // 如果不是promise的實例且包含then方法
                  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.all里面全部為fulFilled狀態(tài)后
              if (--remaining === 0) {
                resolve(args);
              }
            }

        如果傳進來的val(args的每一項)不是對象或者function的話,那么直接視為結(jié)果值把args[i]給替換掉

        args[i] = val;

        如果傳進來的是一個Promise,則

        if (val instanceof Promise && val.then === Promise.prototype.then) {
              ...
        }

        如果傳進來的val不是Promise且包含then方法,則

        else {
          // 如果不是promise的實例且包含then方法
          var then = val.then;
          if (typeof then === 'function') {
            var p = new newPromise(then.bind(val));
            p.then(function (val{
              res(i, val);
            }, reject);
            return;
          }
        }

        重點在這一段

        // _state等于3  證明val實例的值也是一個Promise實例,把val替換成新的Promise實例
          while (val._state === 3) {
            val = val._value;
          }
          // resolved成功調(diào)用,遞歸處理resolved的值
          if (val._state === 1return res(i, val._value);
          if (val._state === 2) reject(val._value);
          // 處于padding狀態(tài)時調(diào)用then方法并手動處理值
          val.then(function (val{
            res(i, val);
          }, reject);
          return;

        只有當Promise的狀態(tài)為Fulfilled的時候,實例的value才會被正確的處理,否則會執(zhí)行return,所以只要有一個Promise未能成功Fulfilled都不會執(zhí)行resolve(args)

        //滿足不了條件
        if (--remaining === 0) {
            resolve(args);
        }

        當?shù)玫剿薪Y(jié)果值的時候(args[i] = val) == args[i] = val.value,調(diào)用resolve方法并傳入結(jié)果數(shù)組args

        看個例子

        const promise2 = new Promise((resolve,reject) => {
            setTimeout(() => {
              resolve('哈哈哈')
            }, 700);
        })
        const promise3 = new Promise((resolve,reject) => {
            setTimeout(() => {
              resolve('哈哈哈2')
            }, 600);
        })
          
        newPromise.all([promise2,promise3])
        .then(res => {
            console.log(res);  //['哈哈哈','哈哈哈2']
        })

        這里無論什么時候resolve,最后得出的結(jié)果數(shù)組res都是按照Promise.all([])數(shù)組里面的順序來輸出的,這也印證了源碼中為什么要把下標傳入到res()的原因(res(i, args[i]))

        Promise.race

        Promise.race = function (values{
          return new Promise(function (resolve, reject{
            iterableToArray(values).forEach(function(value){
              Promise.resolve(value).then(resolve, reject);
            });
          });
        };

        Promise.race傳入一個數(shù)組,用Promise.resolve處理每一項并調(diào)用then方法
        最后返回一個Promise實例
        這里的Promise.resolve(value).then(resolve, reject)當傳入的Promise誰的狀態(tài)首先變?yōu)?code style="margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">Fulfilled,誰就先調(diào)用then
        因為Promise執(zhí)行resolve一次之后狀態(tài)就不會改變,所以race傳入多個Promise,誰的狀態(tài)先變?yōu)?code style="margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">Fulfilled,race就返回哪個

        const promise2 = new Promise((resolve,reject) => {
            setTimeout(() => {
              resolve('哈哈哈')
            }, 700);
        })
        const promise3 = new Promise((resolve,reject) => {
            setTimeout(() => {
              resolve('哈哈哈2')
            }, 600);
        })

        Promise.race([promise2,promise3])
        .then(res => {
            console.log(res);  //哈哈哈2
        })



        如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:

        1. 點個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點在看,都是耍流氓 -_-)
        2. 歡迎加我微信「TH0000666」一起交流學習...
        3. 關注公眾號「前端Sharing」,持續(xù)為你推送精選好文。


        瀏覽 62
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        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>
            娇妻被隔壁老王日出白浆电影 | 九九热久久久99国产盗摄蜜臀 | 久久欧| 女人扒开腿让男人狂桶 | 欧美黄片毛 | 又大又紧又爽水又多18p | 男人操男人的视频 | 波多野结衣操 | 永久免费中文字幕 | 污污视频网站下载 |