<span id="35v3v"><th id="35v3v"></th></span>
<address id="35v3v"></address>

    <address id="35v3v"></address>
      <address id="35v3v"><listing id="35v3v"><meter id="35v3v"></meter></listing></address>
        <listing id="35v3v"><listing id="35v3v"></listing></listing>

        <listing id="35v3v"><listing id="35v3v"><menuitem id="35v3v"></menuitem></listing></listing>

        <noframes id="35v3v"><noframes id="35v3v">
        當前位置: 首頁 / 技術分享 / 正文
        手封MyPromise

        2022-12-02

           mypromise

          引子

          面試時如果被問到“如果沒有Promise,能不能自己實現一個”,就是在深度考察對異步的理解了;

          如果你的回答是“能,我封過”,則必然令人刮目相看~

          事實上,作為ES6新增的API,即使沒有,用戶也完全可以基于想要的效果自己去實現一個;

          這里給到大家我個人的實現:MyPromise;

          源碼地址

          實現思路

          將then/catch/finally中入參的回調函數按順序收集到一個隊列中;

          親自實現一下resolve和reject方法;

          一旦實例resolve就將隊列頭部的catch回調都彈出,一旦實例reject就將隊列頭部的then回調都彈出,然后拿取隊列頭部的回調函數實行起來;

          每次回調的執行結果即返回值需要繼續resolve或reject一下,然后繼續遞歸執行上述過程,直到回調隊列為空;

          接收任務執行器

          當我們new出一個MyPromise,并內置一個任務執行器函數的時候,需要在MyPromise的構造器中接收任務執行器函數,并同步地、立即地將其執行起來;

          要特別注意:任務執行器函數是同步地跑在主線程的,而不屬于異步回調;

          test.js

          new MyPromise(

          /* 任務執行器:2秒后隨機履約或毀約 */

          (resolve, reject) => {

          setTimeout(() => {

          Math.random() > 0.5 ? resolve("data0") : reject(new Error("一張好人卡"));

          }, 2000);

          }

          )

          MyPromise.js

          class MyPromise {

          constructor(executor) {

          /* 接收任務執行器并立即調用(同步調用) */

          this.executor = executor;

          this.executor();

          }

          }

          定義resolve與reject

          任務執行器函數會在稍后履約(resolve)或毀約(reject)

          因此要告訴MyPromise履約或毀約時具體做什么

          這里我們先做一下簡單的打印

          MyPromise.js

          class MyPromise {

          constructor(executor) {

          /* 接收任務執行器并立即調用(同步調用) */

          /* 綁定執行器函數中的resolve與reject */

          this.executor = executor.bind(

          this, //當前promise實例

          

          //當前promise實例執行resolve時,this不變

          MyPromise.doResolve.bind(this),

          

          //當前promise實例執行reject時,this不變

          MyPromise.doReject.bind(this)

          );

          this.executor();

          }

          

          /* promise實例執行resolve */

          static doResolve(data) {

          console.log("doResolve", data);

          }

          

          /* promise實例執行reject */

          static doReject(err) {

          console.log("doReject", err);

          }

          }

          

          module.exports = MyPromise;

          明確回調期望

          明確一下異步任務履約/毀約后我們希望做什么

          我們希望一旦異步任務resolve就執行then中的回調

          我們希望一旦異步任務reject就執行catch中的回調

          我們希望異步任務無論成功還是失敗,最終都執行finally中的回調

          期望如下:test.js

          const MyPromise = require("./MyPromiseX")

          

          new MyPromise(

          /* 任務執行器:2秒后隨機履約或毀約 */

          (resolve, reject) => {

          setTimeout(() => {

          Math.random() > 0.1 ? resolve("data0") : reject(new Error("一張好人卡"));

          }, 2000);

          }

          )

          .then(

          data => {

          console.log("then1:data=",data)

          return "data from then1"

          // throw new Error("err from then1")

          }

          )

          .catch(

          err => {

          console.log("catch1:err=",err)

          return "data from catch1"

          }

          )

          .finally(

          ()=>console.log("finally:game over!")

          )

          收集回調隊列

          每一個then中有一個成功回調

          每一個catch中有一個失敗回調

          finally中有一個最終回調

          我們先按順序將這些回調收集在一個隊列中

          代碼如下:MyPromise.js

          class MyPromise {

          constructor(executor) {

          

          // 回調隊列

          this.callbacks = [];

          

          /* 接收任務執行器并立即調用(同步調用) */

          // 綁定執行器函數中的resolve與reject

          this.executor = executor.bind(

          this, //當前promise實例

          

          //當前promise實例執行resolve時,this不變

          MyPromise.doResolve.bind(this),

          

          //當前promise實例執行reject時,this不變

          MyPromise.doReject.bind(this)

          );

          

          // 立即調用任務執行器

          this.executor();

          }

          

          /* promise實例執行resolve */

          static doResolve(data) {

          console.log("doResolve", data);

          }

          

          /* promise實例執行reject */

          static doReject(err) {

          console.log("doReject", err);

          }

          

          /* 收集成功回調到隊列 */

          then(onData) {

          // 將來一旦Promise毀約 回調隊列頭部的所有then回調都要彈出作廢

          this.callbacks.push({ type: "then", callback: onData });

          return this;

          }

          

          /* 收集失敗回調到隊列 */

          catch(onErr) {

          // 將來一旦Promise履約 回調隊列頭部的所有catch回調都要彈出作廢

          this.callbacks.push({ type: "catch", callback: onErr });

          return this;

          }

          

          /* 收集終點回調到隊列(此處假設只有一個終點回調) */

          finally(onFinish) {

          this.callbacks.push({ type: "finally", callback: onFinish });

          return this;

          }

          }

          

          module.exports = MyPromise;

          此時在測試腳本test.js的末尾打印一下回調隊列的話,你會看到如下輸出:

          [

          { type: 'then', callback: [Function (anonymous)] },

          { type: 'catch', callback: [Function (anonymous)] },

          { type: 'finally', callback: [Function (anonymous)] }

          ]

          如果來上一堆亂序的then/catch/finally的話,則你會看到如下輸出:

          [

          { type: 'then', callback: [Function (anonymous)] },

          { type: 'then', callback: [Function (anonymous)] },

          { type: 'then', callback: [Function (anonymous)] },

          { type: 'then', callback: [Function (anonymous)] },

          { type: 'catch', callback: [Function (anonymous)] },

          { type: 'catch', callback: [Function (anonymous)] },

          { type: 'then', callback: [Function (anonymous)] },

          { type: 'finally', callback: [Function (anonymous)] }

          ]

          連環作案(履約或毀約)

          讓我們在每次成功或失敗后,都隨機地繼續履約或毀約起來!

          test.js

          const MyPromise = require("./MyPromiseX")

          

          const p = new MyPromise(

          /* 任務執行器:2秒后隨機履約或毀約 */

          (resolve, reject) => {

          setTimeout(() => {

          Math.random() > 0.5 ? resolve("data0") : reject(new Error("一張好人卡"));

          }, 2000);

          }

          )

          .then(

          data => {

          console.log("then1:data=",data)

          

          /* 繼續履約或毀約! */

          return "data from then1"

          // throw new Error("err from then1")

          }

          )

          .then(

          data => {

          console.log("then2:data=",data)

          

          /* 繼續履約或毀約! */

          return MyPromise.resolve("data from then2")

          // return MyPromise.reject("err from then2")

          }

          )

          .then(

          data => {

          console.log("then3:data=",data)

          

          /* 繼續履約或毀約! */

          return new MyPromise(

          (resolve,reject) => setTimeout(() => {

          resolve("data from then3")

          // reject("err from then3")

          }, 2000)

          )

          }

          )

          .then(

          /* 這里本質繼續履約了一個undefined */

          data => console.log("then4:data=",data)

          )

          .catch(

          err => {

          console.log("catch1:err=",err)

          

          /* 繼續履約 */

          return "data from catch1"

          }

          )

          .catch(

          /* 這里本質繼續履約了一個undefined */

          err => console.log("catch2:err=",err)

          )

          .then(

          /* 這里本質繼續履約了一個undefined */

          data => console.log("then5:data=",data)

          )

          .finally(

          /* 這里本質繼續履約了一個undefined */

          ()=>console.log("finally:game over!")

          )

          

          // 打印一下MyPromise對象的回調隊列

          console.log("p.callbacks",p.callbacks);

          所謂MyPromise.resolve(data)或MyPromise.reject(err)無非也就是newPromise(executor)的一個語法糖而已,實現如下:

          MyPromise.js片段

          /* 語法糖:創建一個立即resolve的Promise對象 */

          static resolve(data) {

          return new MyPromise((resolve) => resolve(data));

          }

          

          /* 語法糖:創建一個立即reject的Promise對象 */

          static reject(err) {

          return new MyPromise((resolve, reject) => reject(err));

          }

          準備執行連環回調

          我們無非想要如下效果:

          只要前面的環節履約了:在回調隊列中找出下一個type為then的回調執行起來;

          只要前面的環節毀約了:在回調隊列中找出下一個type為catch的回調執行起來;

          找下一個then回調前,所有位于它前面的catch都驅逐先;

          找下一個catch回調前,所有位于它前面的then都驅逐先;

          我們來擼碼實現之!

          定義MyPromise狀態

          class MyPromise {

          /* Promise狀態定義 */

          static STATUS_PENDING = 0; // 掛起態

          static STATUS_FULFILLED = 1; // 履約態

          static STATUS_REJECTED = 2; // 毀約態

          ...

          }

          定義回調入參

          constructor(executor) {

          /* 回調隊列 + 回調入參 (JS單線程模型=每次只能有一個回調被執行) */

          this.callbacks = [];

          

          // 定義給回調傳的入參,無論是數據還是錯誤,我們統稱為cbArg

          this.cbArg = null;

          ...

          }

          定義驅逐器

          成功時:從回調隊列頭部把所有的catch驅逐

          失敗時:從回調隊列頭部把所有的then驅逐

          /*

          成功時:從回調隊列頭部把所有的catch驅逐

          失敗時:從回調隊列頭部把所有的then驅逐

          */

          shiftCallbacksWithType(type) {

          /* 只要回調隊列中還有回調,就把隊列頭部type為指定類型的回調彈出去 */

          while (this.callbacks.length && this.callbacks[0].type === type) {

          // 驅逐一個回調函數

          this.callbacks.shift();

          }

          }

          執行連環回調

          當前任履約時

          狀態設置為履約態

          將回調入參設置為需要履約的數據

          找下一個then里的回調執行起來

          /* promise實例執行resolve */

          static doResolve(data) {

          /* 設置狀態為履約態 + 設置回調時的入參 + 拉起下一次回調 */

          this.status = MyPromise.STATUS_FULFILLED;

          

          // 回調入參為數據

          this.cbArg = data;

          

          // 拉起下一次then回調

          setTimeout(() => {

          this.next();

          });

          }

          當前任毀約時

          狀態設置為毀約態

          將回調入參設置為毀約的原因

          找下一個catch里的回調執行起來

          /* promise實例執行reject */

          static doReject(err) {

          /* 設置狀態為毀約態 + 設置回調時的錯誤 + 拉起下一次回調 */

          this.status = MyPromise.STATUS_REJECTED;

          

          // 回調入參為錯誤

          this.cbArg = err;

          

          // 拉起下一次catch回調

          setTimeout(() => {

          this.next();

          });

          }

          執行下一次回調

          整個MyPromise最核心的邏輯來了

          只要當前MyPromise為履約態,就驅逐隊列頭部所有的catch回調先

          只要當前MyPromise為毀約態,就驅逐隊列頭部所有的then回調先

          拿取隊列頭部的那個回調執行起來

          next() {

          /* 確定該回調哪一個callback */

          if (this.status === MyPromise.STATUS_FULFILLED) {

          // 履約時:彈光隊列頭部的catch回調

          this.shiftCallbacksWithType("catch");

          }

          

          if (this.status === MyPromise.STATUS_REJECTED) {

          // 毀約時:彈光隊列頭部的then回調

          this.shiftCallbacksWithType("then");

          }

          

          /* 如果回調隊列已空,則直接結束程序 */

          if (!this.callbacks.length) {

          console.log("回調隊列已空");

          return;

          }

          

          /* 拉取回調隊列頭部的回調函數(注意這里無視了type只解構出函數本身) */

          let { callback } = this.callbacks.shift();

          // 執行回調并拿到其結果(value或promise對象)

          let value = callback(this.cbArg);

          }

          當回調返回value

          test.js片段

          .then(

          data => {

          console.log("then1:data=",data)

          

          /* 繼續履約或毀約! */

          return "data from then1"

          // throw new Error("err from then1")

          }

          )

          當回調返回一個新的(非MyPromise對象)的普通value時,我們繼續向后resolve它

          程序會繼續doResolve,doResolve內部會繼續拉起下一次next

          下一次next找出的回調還有新的返回值

          遞歸了解一下!歸了解一下!了解一下!解一下!一下!下!

          next() {

          ...

          // 執行回調并拿到其結果(value或promise對象)

          let value = callback(this.cbArg);

          

          /* 如果回調函數返回一個value 繼續向下resolve(value) */

          if (!(value instanceof MyPromise)) {

          MyPromise.doResolve.call(this, value);

          }

          ...

          }

          當回調拋出錯誤時

          test.js片段

          .then(

          data => {

          console.log("then1:data=",data)

          

          /* 繼續履約或毀約! */

          // return "data from then1"

          throw new Error("err from then1")

          }

          )

          try-catch起來,把錯誤信息reject一下

          程序會繼續doReject,doReject內部會繼續拉起下一次next

          下一次next找出的回調還有新的返回值

          遞歸了解一下!歸了解一下!了解一下!解一下!一下!下!

          next() {

          ...

          /* 拉取回調隊列頭部的回調函數(注意這里無視了type只解構出函數本身) */

          let { callback } = this.callbacks.shift();

          

          /* 防止回調函數中throw錯誤 */

          try {

          // 執行回調并拿到其結果(value或promise對象)

          let value = callback(this.cbArg);

          

          /* 如果回調函數返回一個value 繼續向下resolve(value) */

          if (!(value instanceof MyPromise)) {

          MyPromise.doResolve.call(this, value);

          }else {

          ...

          }

          } catch (err) {

          // 回調函數拋出錯誤時相當于reject(err)

          MyPromise.doReject.call(this, err);

          }

          }

          當回調返回一個新的MP對象時

          .then(

          data => {

          console.log("then2:data=",data)

          

          /* 繼續履約或毀約! */

          return MyPromise.resolve("data from then2")

          // return MyPromise.reject("err from then2")

          }

          )

          .then(

          data => {

          console.log("then3:data=",data)

          

          /* 繼續履約或毀約! */

          return new MyPromise(

          (resolve,reject) => setTimeout(() => {

          resolve("data from then3")

          // reject("err from then3")

          }, 2000)

          )

          }

          )

          首先,MP的構造器一定會被調用!

          構造器里有this.executor(),this.executor()里有resolve(data)或reject(err),也就是doResolve(data)或doReject(err)

          不斷遞歸next()找下一次回調的過程會自發地跑起

          這個該死的MP對象可能自帶一個回調隊列

          我們把當前MP對象剩余的回調隊列過戶給它即可

          next() {

          ...

          /* 拉取回調隊列頭部的回調函數(注意這里無視了type只解構出函數本身) */

          let { callback } = this.callbacks.shift();

          

          /* 防止回調函數中throw錯誤 */

          try {

          // 執行回調并拿到其結果(value或promise對象)

          let value = callback(this.cbArg);

          

          /* 如果回調函數返回一個value 繼續向下resolve(value) */

          if (!(value instanceof MyPromise)) {

          ...

          }

          else {

          // 如果回調函數返回一個Promise對象

          // 將后續所有callback過戶給新的Promise對象

          value.callbacks = value.callbacks.concat(this.callbacks);

          }

          } catch (err) {...}

          }

          完整的next函數

          遞歸inside!

          /*

          從回調隊列里拉取下一個適當的callback并回調之

          這是MyPromise的核心代碼:遞歸inside!

          */

          next() {

          /* 確定該回調哪一個callback */

          if (this.status === MyPromise.STATUS_FULFILLED) {

          // 履約時:彈光隊列頭部的catch回調

          this.shiftCallbacksWithType("catch");

          }

          

          if (this.status === MyPromise.STATUS_REJECTED) {

          // 毀約時:彈光隊列頭部的then回調

          this.shiftCallbacksWithType("then");

          }

          

          /* 如果回調隊列已空,則直接結束程序 */

          if (!this.callbacks.length) {

          console.log("回調隊列已空");

          return;

          }

          

          /* 拉取回調隊列頭部的回調函數(注意這里無視了type只解構出函數本身) */

          let { callback } = this.callbacks.shift();

          

          /* 防止回調函數中throw錯誤 */

          try {

          // 執行回調并拿到其結果(value或promise對象)

          let value = callback(this.cbArg);

          

          /* 如果回調函數返回一個value 繼續向下resolve(value) */

          if (!(value instanceof MyPromise)) {

          MyPromise.doResolve.call(this, value);

          }

          else {

          // 如果回調函數返回一個Promise對象

          // 將后續所有callback過戶給新的Promise對象

          value.callbacks = value.callbacks.concat(this.callbacks);

          }

          } catch (err) {

          // 回調函數拋出錯誤時相當于reject(err)

          MyPromise.doReject.call(this, err);

          }

          }

          完整代碼

          MyPromise.js

          class MyPromise {

          /* Promise狀態定義 */

          static STATUS_PENDING = 0; // 掛起態

          static STATUS_FULFILLED = 1; // 履約態

          static STATUS_REJECTED = 2; // 毀約態

          

          constructor(executor) {

          /* 回調隊列 + 回調入參 (JS單線程模型=每次只能有一個回調被執行) */

          this.callbacks = [];

          

          // 定義給回調傳的入參,無論是數據還是錯誤,我們統稱為cbArg

          this.cbArg = null;

          

          /* 綁定執行器函數中的resolve與reject */

          this.executor = executor.bind(

          this, //當前promise實例

          

          //當前promise實例執行resolve時,this不變

          MyPromise.doResolve.bind(this),

          

          //當前promise實例執行reject時,this不變

          MyPromise.doReject.bind(this)

          );

          

          // 執行任務前先將Promise狀態設置為pending

          this.status = MyPromise.STATUS_PENDING;

          

          // 執行任務(任務中會resolve/doResolve或reject/doReject)

          this.executor();

          }

          

          /* promise實例執行resolve */

          static doResolve(data) {

          /* 設置狀態為履約態 + 設置回調時的入參 + 拉起下一次回調 */

          this.status = MyPromise.STATUS_FULFILLED;

          

          // 回調入參為數據

          this.cbArg = data;

          

          // 拉起下一次then回調

          setTimeout(() => {

          this.next();

          });

          }

          

          /* promise實例執行reject */

          static doReject(err) {

          /* 設置狀態為毀約態 + 設置回調時的錯誤 + 拉起下一次回調 */

          this.status = MyPromise.STATUS_REJECTED;

          

          // 回調入參為錯誤

          this.cbArg = err;

          

          // 拉起下一次catch回調

          setTimeout(() => {

          this.next();

          });

          }

          

          /*

          成功時:從回調隊列頭部把所有的catch驅逐

          失敗時:從回調隊列頭部把所有的then驅逐

          */

          shiftCallbacksWithType(type) {

          /* 只要回調隊列中還有回調,就把隊列頭部type為指定類型的回調彈出去 */

          while (this.callbacks.length && this.callbacks[0].type === type) {

          // 驅逐一個回調函數

          this.callbacks.shift();

          }

          }

          

          /*

          從回調隊列里拉取下一個適當的callback并回調之

          這是MyPromise的核心代碼:遞歸inside!

          */

          next() {

          /* 確定該回調哪一個callback */

          if (this.status === MyPromise.STATUS_FULFILLED) {

          // 履約時:彈光隊列頭部的catch回調

          this.shiftCallbacksWithType("catch");

          }

          

          if (this.status === MyPromise.STATUS_REJECTED) {

          // 毀約時:彈光隊列頭部的then回調

          this.shiftCallbacksWithType("then");

          }

          

          /* 如果回調隊列已空,則直接結束程序 */

          if (!this.callbacks.length) {

          console.log("回調隊列已空");

          return;

          }

          

          /* 拉取回調隊列頭部的回調函數(注意這里無視了type只解構出函數本身) */

          let { callback } = this.callbacks.shift();

          

          /* 防止回調函數中throw錯誤 */

          try {

          // 執行回調并拿到其結果(value或promise對象)

          let value = callback(this.cbArg);

          

          /* 如果回調函數返回一個value 繼續向下resolve(value) */

          if (!(value instanceof MyPromise)) {

          MyPromise.doResolve.call(this, value);

          }

          else {

          // 如果回調函數返回一個Promise對象

          // 將后續所有callback過戶給新的Promise對象

          value.callbacks = value.callbacks.concat(this.callbacks);

          }

          } catch (err) {

          // 回調函數拋出錯誤時相當于reject(err)

          MyPromise.doReject.call(this, err);

          }

          }

          

          /* 語法糖:創建一個立即resolve的Promise對象 */

          static resolve(data) {

          return new MyPromise((resolve) => resolve(data));

          }

          

          /* 語法糖:創建一個立即reject的Promise對象 */

          static reject(err) {

          return new MyPromise((resolve, reject) => reject(err));

          }

          

          /* 收集成功回調到隊列 */

          then(onData) {

          // 將來一旦Promise毀約 回調隊列頭部的所有then回調都要彈出作廢

          this.callbacks.push({ type: "then", callback: onData });

          return this;

          }

          

          /* 收集失敗回調到隊列 */

          catch(onErr) {

          // 將來一旦Promise履約 回調隊列頭部的所有catch回調都要彈出作廢

          this.callbacks.push({ type: "catch", callback: onErr });

          return this;

          }

          

          /* 收集終點回調到隊列(此處假設只有一個終點回調) */

          finally(onFinish) {

          this.callbacks.push({ type: "finally", callback: onFinish });

          return this;

          }

          }

          

          module.exports = MyPromise;

          測試代碼test.js

          const MyPromise = require("./MyPromise3")

          

          const p = new MyPromise(

          /* 任務執行器:2秒后隨機履約或毀約 */

          (resolve, reject) => {

          setTimeout(() => {

          Math.random() > 0.1 ? resolve("data0") : reject(new Error("一張好人卡"));

          }, 2000);

          }

          )

          .then(

          data => {

          console.log("then1:data=",data)

          

          /* 繼續履約或毀約! */

          return "data from then1"

          // throw new Error("err from then1")

          }

          )

          .then(

          data => {

          console.log("then2:data=",data)

          

          /* 繼續履約或毀約! */

          return MyPromise.resolve("data from then2")

          // return MyPromise.reject("err from then2")

          }

          )

          .then(

          data => {

          console.log("then3:data=",data)

          

          /* 繼續履約或毀約! */

          return new MyPromise(

          (resolve,reject) => setTimeout(() => {

          resolve("data from then3")

          // reject("err from then3")

          }, 2000)

          )

          }

          )

          .then(

          /* 這里本質繼續履約了一個undefined */

          data => console.log("then4:data=",data)

          )

          .catch(

          err => {

          console.log("catch1:err=",err)

          

          /* 繼續履約 */

          return "data from catch1"

          }

          )

          .catch(

          /* 這里本質繼續履約了一個undefined */

          err => console.log("catch2:err=",err)

          )

          .then(

          /* 這里本質繼續履約了一個undefined */

          data => console.log("then5:data=",data)

          )

          .finally(

          /* 這里本質繼續履約了一個undefined */

          ()=>console.log("finally:game over!")

          )

          

          // 打印一下MyPromise對象的回調隊列

          console.log("p.callbacks",p.callbacks);

          測試效果

          https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5c47910678fd41acba1eed2ea02bbabb~tplv-k3u1fbpfcp-watermark.image?

        好程序員公眾號

        • · 剖析行業發展趨勢
        • · 匯聚企業項目源碼

        好程序員開班動態

        More+
        • HTML5大前端 <高端班>

          開班時間:2021-04-12(深圳)

          開班盛況

          開班時間:2021-05-17(北京)

          開班盛況
        • 大數據+人工智能 <高端班>

          開班時間:2021-03-22(杭州)

          開班盛況

          開班時間:2021-04-26(北京)

          開班盛況
        • JavaEE分布式開發 <高端班>

          開班時間:2021-05-10(北京)

          開班盛況

          開班時間:2021-02-22(北京)

          開班盛況
        • Python人工智能+數據分析 <高端班>

          開班時間:2021-07-12(北京)

          預約報名

          開班時間:2020-09-21(上海)

          開班盛況
        • 云計算開發 <高端班>

          開班時間:2021-07-12(北京)

          預約報名

          開班時間:2019-07-22(北京)

          開班盛況
        在線咨詢
        試聽
        入學教程
        立即報名

        Copyright 2011-2023 北京千鋒互聯科技有限公司 .All Right 京ICP備12003911號-5 京公網安備 11010802035720號

        黑人100部Av解禁片
        <span id="35v3v"><th id="35v3v"></th></span>
        <address id="35v3v"></address>

          <address id="35v3v"></address>
            <address id="35v3v"><listing id="35v3v"><meter id="35v3v"></meter></listing></address>
              <listing id="35v3v"><listing id="35v3v"></listing></listing>

              <listing id="35v3v"><listing id="35v3v"><menuitem id="35v3v"></menuitem></listing></listing>

              <noframes id="35v3v"><noframes id="35v3v">