<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>

          「一次寫(xiě)過(guò)癮」手寫(xiě) Promise 全家桶 + Generator + async/await

          共 34830字,需瀏覽 70分鐘

           ·

          2021-03-01 13:58

          (給全棧前端精選加星標(biāo),提升前端技能


          轉(zhuǎn)自:前端食堂

          手寫(xiě) Promise 全家桶

          Promise/A+ 規(guī)范[4]鎮(zhèn)樓!

          如果你沒(méi)讀過(guò) Promise/A+ 規(guī)范也沒(méi)關(guān)系,我?guī)湍憧偨Y(jié)了如下三部分重點(diǎn):

          不過(guò)建議看完本文后還是要親自去讀一讀,不多 bb,開(kāi)始展示。



          規(guī)范重點(diǎn)

          1.Promise 狀態(tài)

          Promise 的三個(gè)狀態(tài)分別是 pendingfulfilled 和 rejected

          • pending: 待定,Promise 的初始狀態(tài)。在此狀態(tài)下可以落定 (settled) 為 fulfilled 或 rejected狀態(tài)。
          • fulfilled: 兌現(xiàn)(解決),表示執(zhí)行成功。Promise 被 resolve 后的狀態(tài),狀態(tài)不可再改變,且有一個(gè)私有的值 value。
          • rejected: 拒絕,表示執(zhí)行失敗。Promise 被 reject 后的狀態(tài),狀態(tài)不可再改變,且有一個(gè)私有的原因 reason。

          注意:value 和 reason 也是不可變的,它們包含原始值或?qū)ο蟮牟豢尚薷牡囊茫J(rèn)值為 undefined

          2.Then 方法

          要求必須提供一個(gè) then 方法來(lái)訪問(wèn)當(dāng)前或最終的 value 或 reason。

          promise.then(onFulfilled, onRejected)
          • 1.then 方法接受兩個(gè)函數(shù)作為參數(shù),且參數(shù)可選。
          • 2.如果可選參數(shù)不為函數(shù)時(shí)會(huì)被忽略。
          • 3.兩個(gè)函數(shù)都是異步執(zhí)行,會(huì)放入事件隊(duì)列等待下一輪 tick。
          • 4.當(dāng)調(diào)用 onFulfilled 函數(shù)時(shí),會(huì)將當(dāng)前 Promise 的 value 值作為參數(shù)傳入。
          • 5.當(dāng)調(diào)用 onRejected 函數(shù)時(shí),會(huì)將當(dāng)前 Promise 的 reason 失敗原因作為參數(shù)傳入。
          • 6.then 函數(shù)的返回值為 Promise。
          • 7.then 可以被同一個(gè) Promise 多次調(diào)用。

          3.Promise 解決過(guò)程

          Promise 的解決過(guò)程是一個(gè)抽象操作,接收一個(gè) Promise 和一個(gè)值 x。

          針對(duì) x 的不同值處理以下幾種情況:

          • 1.x 等于 Promise

          拋出 TypeError 錯(cuò)誤,拒絕 Promise。

          • 2.x 是 Promise 的實(shí)例

          如果 x 處于待定狀態(tài),那么 Promise 繼續(xù)等待直到 x 兌現(xiàn)或拒絕,否則根據(jù) x 的狀態(tài)兌現(xiàn)/拒絕 Promise。

          • 3.x 是對(duì)象或函數(shù)

          取出 x.then 并調(diào)用,調(diào)用時(shí)將 this 指向 x。將 then 回調(diào)函數(shù)中得到的結(jié)果 y 傳入新的 Promise 解決過(guò)程中,遞歸調(diào)用。

          如果執(zhí)行報(bào)錯(cuò),則將以對(duì)應(yīng)的失敗原因拒絕 Promise。

          這種情況就是處理?yè)碛?then() 函數(shù)的對(duì)象或函數(shù),我們也叫它 thenable

          • 4.如果 x 不是對(duì)象或函數(shù)

          以 x 作為值執(zhí)行 Promise。

          手寫(xiě) Promise

          1.首先定義 Promise 的三個(gè)狀態(tài)

          var PENDING = 'pending';
          var FULFILLED = 'fulfilled';
          var REJECTED = 'rejected';

          2.我們?cè)賮?lái)搞定 Promise 的構(gòu)造函數(shù)

          創(chuàng)建 Promise 時(shí)需要傳入 execute 回調(diào)函數(shù),接收兩個(gè)參數(shù),這兩個(gè)參數(shù)分別用來(lái)兌現(xiàn)和拒絕當(dāng)前 Promise。

          所以我們需要定義 resolve() 和 reject() 函數(shù)。

          初始狀態(tài)為 PENDING,在執(zhí)行時(shí)可能會(huì)有返回值 value,在拒絕時(shí)會(huì)有拒絕原因 reason

          同時(shí)需要注意,Promise 內(nèi)部的異常不能直接拋出,需要進(jìn)行異常捕獲。

          function Promise(execute{
              var that = this;
              that.state = PENDING;
              function resolve(value{
                  if (that.state === PENDING) {
                      that.state = FULFILLED;
                      that.value = value;
                  }
              }
              function reject(reason{
                  if (that.state === PENDING) {
                      that.state = REJECTED;
                      that.reason = reason;
                  }
              }
              try {
                  execute(resolve, reject);
              } catch (e) {
                  reject(e);
              }
          }

          3. 實(shí)現(xiàn) then() 方法

          then 方法用來(lái)注冊(cè)當(dāng)前 Promise 狀態(tài)落定后的回調(diào),每個(gè) Promise 實(shí)例都需要有它,顯然要寫(xiě)到 Promise 的原型 prototype 上,并且 then() 函數(shù)接收兩個(gè)回調(diào)函數(shù)作為參數(shù),分別是 onFulfilled 和 onRejected

          Promise.prototype.then = function(onFulfilled, onRejected{}

          根據(jù)上面第 2 條規(guī)則,如果可選參數(shù)不為函數(shù)時(shí)應(yīng)該被忽略,我們要對(duì)參數(shù)進(jìn)行如下判斷。

          onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(xreturn x; }
          onRejected = typeof onRejected === 'function' ? onRejected : function(ethrow e; }

          根據(jù)第 3 條規(guī)則,需要使用 setTimeout 延遲執(zhí)行,模擬異步。

          根據(jù)第 4 條、第 5 條規(guī)則,需要根據(jù) Promise 的狀態(tài)來(lái)執(zhí)行對(duì)應(yīng)的回調(diào)函數(shù)。

          在 PENDING 狀態(tài)時(shí),需要等到狀態(tài)落定才能調(diào)用。我們可以將 onFulfilled 和 onRejected 函數(shù)存到 Promise 的屬性 onFulfilledFn 和 onRejectedFn 中,

          當(dāng)狀態(tài)改變時(shí)分別調(diào)用它們。

          var that = this;
          var promise;
          if (that.state === FULFILLED) {
              setTimeout(function() {
                  onFulfilled(that.value);
              });
          }
          if (that.state === REJECTED) {
              setTimeout(function() {
                  onRejected(that.reason);
              });
          }
          if (that.state === PENDING) {
               that.onFulfilledFn = function() {
                  onFulfilled(that.value);
              }
              that.onRejectedFn = function() {
                  onRejected(that.reason);
              }
          }

          根據(jù)第 6 條規(guī)則,then 函數(shù)的返回值為 Promise,我們分別給每個(gè)邏輯添加并返回一個(gè) Promise。

          同時(shí),then 支持鏈?zhǔn)秸{(diào)用,我們需要將 onFulfilledFn 和 onRejectedFn 改成數(shù)組。

          var that = this;
          var promise;
          if (that.state === FULFILLED) {
              promise = new Promise(function(resolve, reject{
                  setTimeout(function() {
                      try {
                          onFulfilled(that.value);
                      } catch (reason) {
                          reject(reason);
                      }
                  });
              });
          }
          if (that.state === REJECTED) {
              promise = new Promise(function(resolve, reject{
                  setTimeout(function() {
                      try {
                          onRejected(that.reason);
                      } catch (reason) {
                          reject(reason);
                      }
                  });
              });
          }
          if (that.state === PENDING) {
              promise = new Promise(function(resolve, reject{
                  that.onFulfilledFn.push(function() {
                      try {
                          onFulfilled(that.value);
                      } catch (reason) {
                          reject(reason);
                      }
                  })
                  that.onRejectedFn.push(function() {
                      try {
                          onRejected(that.reason);
                      } catch (reason) {
                          reject(reason);
                      }
                  });
              });
          }

          與上面相對(duì)應(yīng)的,再將 Promise 的構(gòu)造函數(shù)相應(yīng)的進(jìn)行改造。

          • 1.添加 onFulFilledFn 和 onRejectedFn 數(shù)組。

          • 2.resolve() 和 reject() 函數(shù)改變狀態(tài)時(shí),需要異步調(diào)用數(shù)組中的函數(shù),同樣使用 setTimeout 來(lái)模擬異步。

          function Promise(execute{
              var that = this;
              that.state = PENDING;
              that.onFulfilledFn = [];
              that.onRejectedFn = [];

              function resolve(value{
                  setTimeout(function() {
                      if (that.state === PENDING) {
                          that.state = FULFILLED;
                          that.value = value;
                          that.onFulfilledFn.forEach(function(fn{
                              fn(that.value);
                          })
                      }
                  })
              }
              function reject(reason{
                  setTimeout(function() {
                      if (that.state === PENDING) {
                          that.state = REJECTED;
                          that.reason = reason;
                          that.onRejectedFn.forEach(function(fn{
                              fn(that.reason);
                          })
                      }
                  })
              }
              try {
                  execute(resolve, reject);
              } catch (e) {
                  reject(e);
              }
          }

          4.Promise 解決過(guò)程 resolvePromise()

          Promise 解決過(guò)程分為以下幾種情況,我們需要分別進(jìn)行處理:

          1.x 等于 Promise TypeError 錯(cuò)誤

          此時(shí)相當(dāng)于 Promise.then 之后 return 了自己,因?yàn)?then 會(huì)等待 return 后的 Promise,導(dǎo)致自己等待自己,一直處于等待。。

          function resolvePromise(promise, x{
              if (promise === x) {
                  return reject(new TypeError('x 不能等于 promise'));
              }
          }

          2.x 是 Promise 的實(shí)例

          如果 x 處于待定狀態(tài),Promise 會(huì)繼續(xù)等待直到 x 兌現(xiàn)或拒絕,否則根據(jù) x 的狀態(tài)兌現(xiàn)/拒絕 Promise。

          我們需要調(diào)用 Promise 在構(gòu)造時(shí)的函數(shù) resolve() 和 reject() 來(lái)改變 Promise 的狀態(tài)。

          function resolvePromise(promise, x, resolve, reject{
              // ...
              if (x instanceof Promise) {
                  if (x.state === FULFILLED) {
                      resolve(x.value);
                  } else if (x.state === REJECTED) {
                      reject(x.reason);
                  } else {
                      x.then(function(y{
                          resolvePromise(promise, y, resolve, reject);
                      }, reject);
                  }
              }
          }

          3.x 是對(duì)象或函數(shù)

          取出 x.then 并調(diào)用,調(diào)用時(shí)將 this 指向 x,將 then 回調(diào)函數(shù)中得到的結(jié)果 y 傳入新的 Promise 解決過(guò)程中,遞歸調(diào)用。

          如果執(zhí)行報(bào)錯(cuò),則將以對(duì)應(yīng)的失敗原因拒絕 Promise。

          x 可能是一個(gè) thenable 而非真正的 Promise。

          需要設(shè)置一個(gè)變量 executed 避免重復(fù)調(diào)用。

          function resolvePromise(promise, x, resolve, reject{
              // ...
              if ((x !== null) && ((typeof x === 'object' || (typeof x === 'function'))) {
                  var executed;
                  try {
                      var then = x.then;
                      if (typeof then === 'function') {
                          then.call(x, function(y{
                              if (executed) return;
                              executed = true;
                              return resolvePromise(promise, y, resolve, reject);
                          }, function (e{
                              if (executed) return;
                              executed = true;
                              reject(e);
                          }) 
                      } else {
                          resolve(x);
                      }
                  } catch (e) {
                      if (executed) return;
                      executed = true;
                      reject(e);
                  }
              }
          }

          4.直接將 x 作為值執(zhí)行

          function resolvePromise(promise, x, resolve, reject{
              // ...
              resolve(x)
          }

          測(cè)試

          // 為了支持測(cè)試,將模塊導(dǎo)出
          module.exports = {
            deferred() {
              var resolve;
              var reject;
              var promise = new Promise(function (res, rej{
                resolve = res;
                reject = rej;
              })
              return {
                promise,
                resolve,
                reject
              }
            }
          }

          我們可以選用這款測(cè)試工具對(duì)我們寫(xiě)的 Promise 進(jìn)行測(cè)試 Promise/A+ 測(cè)試工具: promises-aplus-tests[5]

          目前支持 827 個(gè)測(cè)試用例,我們只需要在導(dǎo)出模塊的時(shí)候遵循 CommonJS 規(guī)范,按照要求導(dǎo)出對(duì)應(yīng)的函數(shù)即可。



          Promise.resolve

          Promise.resolve() 可以實(shí)例化一個(gè)解決(fulfilled) 的 Promise。

          Promise.resolve = function(value{
              if (value instanceof Promise) {
                  return value;
              }

              return new Promise(function(resolve, reject{
                  resolve(value);
              });
          }

          Promise.reject

          Promise.reject() 可以實(shí)例化一個(gè) rejected 的 Promise 并拋出一個(gè)異步錯(cuò)誤(這個(gè)錯(cuò)誤不能通過(guò)try/catch捕獲,只能通過(guò)拒絕處理程序捕獲)

          Promise.reject = function(reason{
              return new Promise(function(resolve, reject{
                  reject(reason);
              });
          }

          Promise.prototype.catch

          Promise.prototype.catch() 方法用于給 Promise 添加拒絕時(shí)的回調(diào)函數(shù)。

          Promise.prototype.catch = function(onRejected{
              return this.then(null, onRejected);
          }

          Promise.prototype.finally

          Promise.prototype.finally() 方法用于給 Promise 添加一個(gè)不管最終狀態(tài)如何都會(huì)執(zhí)行的操作。

          Promise.prototype.finally = function(fn{
              return this.then(function(value{
                  return Promise.resolve(value).then(function() {
                      return value;
                  });
              }, function(error{
                  return Promise.resolve(reason).then(function() {
                      throw error;
                  });
              });
          }

          Promise.all

          Promise.all() 方法會(huì)將多個(gè) Promise 實(shí)例組合成一個(gè)新的 Promise 實(shí)例。

          組合后的 Promise 實(shí)例只有當(dāng)每個(gè)包含的 Promise 實(shí)例都解決(fulfilled)后才解決(fulfilled),如果有一個(gè)包含的 Promise 實(shí)例拒絕(rejected)了,則合成的 Promise 也會(huì)拒絕(rejected)。

          兩個(gè)注意點(diǎn):

          • 傳入的是可迭代對(duì)象,用 for...of 遍歷 Iterable 更安全
          • 傳入的每個(gè)實(shí)例不一定是 Promise,需要用 Promise.resolve() 包裝
          Promise.all = function(promiseArr{
              return new Promise(function(resolve, reject{
                  const length = promiseArr.length;
                  const result = [];
                  let count = 0;
                  if (length === 0) {
                      return resolve(result);
                  }

                  for (let item of promiseArr) {
                      Promise.resolve(item).then(function(data{
                          result[count++] = data;
                          if (count === length) {
                              resolve(result);
                          }
                      }, function(reason{
                          reject(reason);
                      });
                  }
              });
          }

          Promise.race

          Promise.race() 同樣返回一個(gè)合成的 Promise 實(shí)例,其會(huì)返回這一組中最先解決(fulfilled)或拒絕(rejected)的 Promise 實(shí)例的返回值。

          Promise.race = function(promiseArr{
              return new Promise(function(resolve, reject{
                  const length = promiseArr.length;
                  if (length === 0) {
                      return resolve();
                  } 

                  for (let item of promiseArr) {
                      Promise.resolve(item).then(function(value{
                          return resolve(value);
                      }, function(reason{
                          return reject(reason);
                      });
                  }
              });
          }

          Promise.any

          Promise.any() 相當(dāng)于 Promise.all() 的反向操作,同樣返回一個(gè)合成的 Promise 實(shí)例,只要其中包含的任何一個(gè) Promise 實(shí)例解決(fulfilled)了,合成的 Promise 就解決(fulfilled)。

          只有當(dāng)每個(gè)包含的 Promise 都拒絕(rejected)了,合成的 Promise 才拒絕(rejected)。



          Promise.any = function(promiseArr{
              return new Promise(function(resolve, reject{
                  const length = promiseArr.length;
                  const result = [];
                  let count = 0;
                  if (length === 0) {
                      return resolve(result);
                  } 

                  for (let item of promiseArr) {
                      Promise.resolve(item).then((value) => {
                          return resolve(value);
                      }, (reason) => {
                          result[count++] = reason;
                          if (count === length) {
                              reject(result);
                          }
                      });
                  }
              });
          }

          Promise.allSettled

          Promise.allSettled() 方法也是返回一個(gè)合成的 Promise,不過(guò)只有等到所有包含的每個(gè) Promise 實(shí)例都返回結(jié)果落定時(shí),不管是解決(fulfilled)還是拒絕(rejected),合成的 Promise 才會(huì)結(jié)束。一旦結(jié)束,狀態(tài)總是 fulfilled。

          其返回的是一個(gè)對(duì)象數(shù)組,每個(gè)對(duì)象表示對(duì)應(yīng)的 Promise 結(jié)果。

          對(duì)于每個(gè)結(jié)果對(duì)象,都有一個(gè) status 字符串。如果它的值為 fulfilled,則結(jié)果對(duì)象上存在一個(gè) value 。如果值為 rejected,則存在一個(gè) reason 。

          Promise.allSettled = function(promiseArr{
            return new Promise(function(resolve{
              const length = promiseArr.length;
              const result = [];
              let count = 0;

              if (length === 0) {
                return resolve(result);
              } else {
                for (let item of promiseArr) {
                  Promise.resolve(item).then((value) => {
                      result[count++] = { status'fulfilled'value: value };
                      if (count === length) {
                          return resolve(result);
                      }
                  }, (reason) => {
                      result[count++] = { status'rejected'reason: reason };
                      if (count === length) {
                          return resolve(result);
                      }
                  });
                }
              }
            });
          }


          // 使用 Promise.finally 實(shí)現(xiàn)
          Promise.allSettled = function(promises{
              // 也可以使用擴(kuò)展運(yùn)算符將 Iterator 轉(zhuǎn)換成數(shù)組
              // const promiseArr = [...promises]
              const promiseArr = Array.from(promises)
              return new Promise(resolve => {
                  const result = []
                  const len = promiseArr.length;
                  let count = len;
                  if (len === 0) {
                    return resolve(result);
                  }
                  for (let i = 0; i < len; i++) {
                      promiseArr[i].then((value) => {
                          result[i] = { status'fulfilled'value: value };
                      }, (reason) => {
                          result[i] = { status'rejected'reason: reason };
                      }).finally(() => { 
                          if (!--count) {
                              resolve(result);
                          }
                      });
                  }
              });
          }



          // 使用 Promise.all 實(shí)現(xiàn)
          Promise.allSettled = function(promises{
              // 也可以使用擴(kuò)展運(yùn)算符將 Iterator 轉(zhuǎn)換成數(shù)組
              // const promiseArr = [...promises]
              const promiseArr = Array.from(promises)
              return Promise.all(promiseArr.map(p => Promise.resolve(p).then(res => {
                return { status'fulfilled'value: res }
              }, error => {
                return { status'rejected'reason: error }
              })));
          };

          手寫(xiě) Generator 函數(shù)

          先來(lái)簡(jiǎn)單回顧下 Generator 的使用:

          functionwebCanteenGenerator() {
              yield '店小二兒,給我切兩斤牛肉來(lái)';
              yield '再來(lái)十八碗酒';
              return '好酒!這酒有力氣!';
          }

          var canteen = webCanteenGenerator();
          canteen.next();
          canteen.next();
          canteen.next();
          canteen.next();

          // {value: "店小二兒,給我切兩斤牛肉來(lái)", done: false}
          // {value: "再來(lái)十八碗酒", done: false}
          // {value: "好酒!這酒有力氣!", done: true}
          // {value: undefined, done: true}



          // 簡(jiǎn)易版
          // 定義生成器函數(shù),入?yún)⑹侨我饧?/span>
          function webCanteenGenerator(list{
              var index = 0;
              var len = list.length;
              return {
                  // 定義 next 方法
                  // 記錄每次遍歷位置,實(shí)現(xiàn)閉包,借助自由變量做迭代過(guò)程中的“游標(biāo)”
                  nextfunction() {
                      var done = index >= len; // 如果索引還沒(méi)有超出集合長(zhǎng)度,done 為 false
                      var value = !done ? list[index++] : undefined// 如果 done 為 false,則可以繼續(xù)取值
                      // 返回遍歷是否完畢的狀態(tài)和當(dāng)前值
                      return {
                          done: done,
                          value: value
                      }
                  }
              }
          }

          var canteen = webCanteenGenerator(['道路千萬(wàn)條''安全第一條''行車(chē)不規(guī)范']);
          canteen.next();
          canteen.next();
          canteen.next();

          // {done: false, value: "道路千萬(wàn)條"}
          // {done: false, value: "安全第一條"}
          // {done: false, value: "行車(chē)不規(guī)范"}
          // {done: true, value: undefined}



          手寫(xiě) async/await

          Generator 缺陷:

          • 1.函數(shù)外部無(wú)法捕獲異常
          • 2.多個(gè) yield 會(huì)導(dǎo)致調(diào)試?yán)щy

          async 函數(shù)對(duì) Generator 函數(shù)改進(jìn)如下:

          • 1.內(nèi)置執(zhí)行器
          • 2.更好的語(yǔ)義
          • 3.更廣的適用性
          • 4.返回值是 Promise

          async/await 做的事情就是將 Generator 函數(shù)轉(zhuǎn)換成 Promise,說(shuō)白了,async 函數(shù)就是 Generator 函數(shù)的語(yǔ)法糖,await 命令就是內(nèi)部 then 命令的語(yǔ)法糖。

          const fetchData = (data) => new Promise((resolve) => setTimeout(resolve, 1000, data + 1))

          const fetchResult = async function () {
              var result1 = await fetchData(1);
              var result2 = await fetchData(result1);
              var result3 = await fetchData(result2);
              console.log(result3);
          }

          fetchResult();

          可以嘗試通過(guò) Babel[8] 官網(wǎng)轉(zhuǎn)換一下上述代碼,可以看到其核心就是 _asyncToGenerator 方法。

          我們下面來(lái)實(shí)現(xiàn)它。

          function asyncToGenerator(generatorFn{
              // 將 Generator 函數(shù)包裝成了一個(gè)新的匿名函數(shù),調(diào)用這個(gè)匿名函數(shù)時(shí)返回一個(gè) Promise
              return function() {
                  // 生成迭代器,相當(dāng)于執(zhí)行 Generator 函數(shù)
                  // 如上面三碗不過(guò)崗例子中的 var canteen = webCanteenGenerator()
                  var gen = generatorFn.apply(thisarguments);
                  return new Promise(function(resolve, reject{
                      // 利用 Generator 分割代碼片段,每一個(gè) yield 用 Promise 包裹起來(lái)
                      // 遞歸調(diào)用 Generator 函數(shù)對(duì)應(yīng)的迭代器,當(dāng)?shù)鲌?zhí)行完成時(shí)執(zhí)行當(dāng)前的 Promise,失敗時(shí)則拒絕 Promise
                      function step(key, arg{
                          try {
                              var info = gen[key](arg "key");
                              var value = info.value;
                          } catch (error) {
                              reject(error);
                              return;
                          }

                          if (info.done) {
                              // 遞歸終止條件,完成了就 resolve
                              resolve(value);
                          } else {
                              return Promise.resolve(value).then(function(value{
                                  step('next', value);
                              }, function(err{
                                  step('throw', err);
                              });
                          }
                      }
                      return step('next');
                  });
              }
          }

          好了,本文到這里就告一段落,如果上述代碼你發(fā)現(xiàn)有問(wèn)題的地方,可以在評(píng)論區(qū)留言,一起探討學(xué)習(xí)。

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

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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麻豆天美成人电影 |