巨乳在线中文字幕,巨乳诱惑中文字幕,中文字幕人成乱码熟女_腾讯视频
  • 當前位置: 首頁 / 技術分享 / 正文
    手封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號

    巨乳在线中文字幕,巨乳诱惑中文字幕,中文字幕人成乱码熟女_腾讯视频