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

          共 27845字,需瀏覽 56分鐘

           ·

          2021-05-01 10:37

          前言

          什么是Promise?

          相信許多從事前端的小伙伴們都用過Promise,為了解決異步編程的弊端(地獄回調(diào)等問題),ES6提供了一個強大的東西,那就是Promise。Promise是將異步任務(wù)轉(zhuǎn)換為同步任務(wù)的一個構(gòu)造函數(shù),通過resolve,reject改變?nèi)蝿?wù)的狀態(tài),必不可少的then方法用來收Promise的值,這些都是Promise的基本使用。那么Promise是如何處理狀態(tài)的,又是如何實現(xiàn)resove,reject方法的,又是如何實現(xiàn)鏈?zhǔ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;

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

          doResolve方法

          function doResolve(fn, promise{
            // done防止重復(fù)觸發(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方法
          當(dāng)實例調(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當(dāng)作構(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);
          }

          當(dāng)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方法在這里的作用就相當(dāng)于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的值永遠(yuǎn)只會=1,只會在’if(self._deferredState === 1){}‘的這個判斷里面去執(zhí)行
          到這里小伙伴們又會說,不對啊,我這個不是在同一個Promise對象下嗎,我不是只實例化了一次嗎?
          這里在使用Promise的時候確實是只實例化了一次,但是每次調(diào)用then方法返回的Promise跟實例的Promise并不是同一個引用,也就是說,這里的self并不是實例出來的對象,后面會詳細(xì)介紹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);
            }
            // 當(dāng)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);
          }

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

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

          鏈?zhǔn)秸{(diào)用

          細(xì)心的你可能已經(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的鏈?zhǔn)秸{(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ù)為下標(biāo),第二個參數(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;

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

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

          當(dāng)?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ù)組里面的順序來輸出的,這也印證了源碼中為什么要把下標(biāo)傳入到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)當(dāng)傳入的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」一起交流學(xué)習(xí)...
          3. 關(guān)注公眾號「前端Sharing」,持續(xù)為你推送精選好文。


          瀏覽 62
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  久久99人妻无码精品 | 国产午夜黄色视频在线观看 | 狠操视频| 少妇福利在线 | 99精品在线观看 |