欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript?Promise執(zhí)行流程深刻理解

 更新時間:2022年06月24日 09:33:40   作者:??陳梵阿????  
這篇文章主要介紹了JavaScript?Promise執(zhí)行流程深刻理解,他是一個構(gòu)造函數(shù),每個創(chuàng)建的promise都有各自狀態(tài)和值,且狀態(tài)初始值為pending,值為undefined

手撕Promise

手寫一個Promise已經(jīng)是一個常見的手寫功能了,雖然實際工作上可能并不會用到。但是在面試時還是會經(jīng)常被提起的。

看完收獲

  • 實現(xiàn)一個手寫promise
  • 對promise執(zhí)行流程有著更深刻的理解
  • 從底層理解proimse,應對各種面試題

Promise分析

作用

通過鏈式調(diào)用的方式,解決回調(diào)地獄的問題。

特點

是ES6中新增的引用類型,通過new關(guān)鍵詞來創(chuàng)建實例。

存在3個狀態(tài):

  • pending
  • fulfilled
  • rejected

且狀態(tài)變成fulfilled或者rejected后不可再次變更,具有不可逆性。

new Promise創(chuàng)建實例的時候必須要傳入一個函數(shù),且這個函數(shù)會有2個參數(shù)resolvereject

深入學習JavaScript中的promise

總體實現(xiàn)

首先我們可以按照Promise的特點來實現(xiàn)一個簡單的總體結(jié)構(gòu)。

  • 他是一個構(gòu)造函數(shù),每個創(chuàng)建的promise都有各自狀態(tài)和值,且狀態(tài)初始值為pending,值為undefined。
  • 創(chuàng)建實例的時候需要傳入一個函數(shù),而這個函數(shù)可以接受2個函數(shù)作為參數(shù),這2個函數(shù)都有一個參數(shù),且都可以更改實例的狀態(tài)和值。
  • 根據(jù)Promise的結(jié)構(gòu),我們可以發(fā)現(xiàn)then()、catch()方法在它的原型上。
  function MyPromise(fn) {
      this.PromiseState = 'pending'
      this.PromiseResult = undefined;
      function resolve(data) {
      }
      function reject(error) {

      }
  }
  MyPromise.prototype.then = function(thenCallback) {
  }
  MyPromise.prototype.catch = function(catchCallback) {
  }

上述代碼很簡單,就定義了一個Promise的構(gòu)造函數(shù),有2個屬性(PromiseState、PromiseResult)和2個方法(resolve、reject)。

原型上增加了then方法和catch方法,且這2個方法都接收一個函數(shù)。

而每次通過new來創(chuàng)建Promise的函數(shù)時,傳入的函數(shù)會執(zhí)行,因此我們直接調(diào)用fn函數(shù),并傳入resolve和reject這2個函數(shù)。

resolve和reject初步實現(xiàn)

resolve的調(diào)用會更改promise的狀態(tài)和值,且狀態(tài)是不可逆的。也就是說,只能從pending變成fulfilled或者rejected。而resolve函數(shù)的參數(shù)值會變成promise實例的值,reject同理。 所以我們把resolve和reject簡單的寫完整.

  function MyPromise(fn) {
    this.PromiseState = "pending";
    this.PromiseResult = undefined;
    // 保存實例對象的this的值
    const self = this;
    function resolve(data) { // 如果不使用self,這里內(nèi)部的this會指向window
      // 如果當前的promise實例不是pending的狀態(tài)就退出了,否則就更改當前的promise實例的狀態(tài)和值
      if (self.PromiseState !== "pending") {
        return;
      }
      // 1.修改對象的狀態(tài)([[promiseState]])
      // 2.設置對象結(jié)果值([[promiseResult]])
      self.PromiseState = "fulfilled";
      self.PromiseResult = data;
    }
    function reject(error) {
      if (self.PromiseState !== "pending") {
        return;
      }
      self.PromiseState = "rejected";
      self.PromiseResult = error;
    }
    fn(resolve, reject);
  }

上面代碼中,resolve和reject就是用來更改當前實例的狀態(tài)的,如果當前狀態(tài)已經(jīng)改變了,即不為pending,那么就不再進行狀態(tài)變更和值變更了。

接下來就是驗證一下:

  const p = new MyPromise((resolve, reject) => {
    resolve(1);
    reject(2);
  });
  console.log(p);

發(fā)現(xiàn)我們執(zhí)行了resolve后再執(zhí)行reject并不會再次更改狀態(tài)。

但是還有一點,如果我們不執(zhí)行resolve或者reject,而是直接執(zhí)行throw呢?

先看看原生的Promise效果。

  const p1 = new Promise((resolve, reject) => {
    throw 111;
  });
  console.log(p1);

發(fā)現(xiàn)狀態(tài)會變成rejected。

那我們是不是可以在執(zhí)行fn(resolve,reject)的時候進行異常處理,將狀態(tài)變成rejected,值變成該異常信息。

    // 如果throw異常,就需要捕獲到異常然后更改狀態(tài)
    try {
      // 同步調(diào)用執(zhí)行器函數(shù)
      fn(resolve, reject);
    } catch (e) {
      // 修改promise對象的狀態(tài)為失敗
      reject(e);
    }

then方法的實現(xiàn)

then方法中我們知道它有2個參數(shù),且這2個參數(shù)都是函數(shù),第一個參數(shù)會在promise執(zhí)行resolve時調(diào)用,第二個參數(shù)會在promise執(zhí)行reject時調(diào)用。而.catch只不過是調(diào)用.then第二個參數(shù)的語法糖而已。

    const p = new Promise((resolve, reject) => {
    resolve(1);
  });
  const p1 = p.then(
    (res) => {
      console.log(res, "成功");
    },
    (err) => {
      console.log(err, "失敗");
    }
  );
  console.log(p, 111);
  console.log(p1, 222);

但是我們查看原生的Promise后,可以發(fā)現(xiàn),.then的執(zhí)行會返回一個新的Promise實例。而決定調(diào)用.then的第幾個參數(shù),則是根據(jù)調(diào)用then的那個promise實例的狀態(tài)決定。

修改代碼如下:

  MyPromise.prototype.then = function (thenCallback, catchCallback) {
    return new Promise((resolve, reject) => {
      // 調(diào)用回調(diào)函數(shù),要根據(jù)當前的promise實例來調(diào)用
      if (this.PromiseState === "fulfilled") {
        const result = thenCallback(this.PromiseResult);
        resolve(result);
      }
      if (this.PromiseState === "rejected") {
        const result = catchCallback(this.PromiseResult);
        resolve(result);
      }
    });
  };

我們的自定義函數(shù)上的原型的then返回了一個新的promise實例,而新的promise實例調(diào)用then的第幾個參數(shù)則根據(jù)調(diào)用then的promise實例的狀態(tài)決定(此處的this指向調(diào)用then的那個promise實例)。

我們需要拿到回調(diào)函數(shù)的執(zhí)行結(jié)果,然后再放入新的promise實例的resolve中更改新實例的狀態(tài)和值。

查看一下我們自己的效果:

  const p = new MyPromise((resolve, reject) => {
    reject(1);
  });
  const p1 = p.then(
    (res) => {
      console.log(res, "成功");
    },
    (err) => {
      console.log(err, "失敗");
    }
  );
  console.log(p, 111);
  console.log(p1, 222);

這時候可能有人就會提出疑問,如果在上述的p中的回調(diào)里延時執(zhí)行resolve或者reject呢?

  const p = new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(1);
    }, 1000);
  });
  const p1 = p.then(
    (res) => {
      console.log(res, "成功");
    },
    (err) => {
      console.log(err, "失敗");
    }
  );
  console.log(p, 111);
  console.log(p1, 222);

我們可以發(fā)現(xiàn)p1的狀態(tài)變成了pending。

這是因為當執(zhí)行到setTimeout的時候,發(fā)現(xiàn)是一個異步函數(shù),然后會將這個函數(shù)掛起,繼續(xù)執(zhí)行下面的.then(),然后當時的p的狀態(tài)是pendding,因為不會執(zhí)行任何代碼。

那我們當然是要加一個pendding的判斷啦!

可是該怎么實現(xiàn)判斷的內(nèi)部代碼呢?

首先我們先來思考一下,promise是一個對象,他是按地址引用的。我們希望在定時器時間到的時候再去更改這個新的promise的狀態(tài)和值。因此我們需要訪問到上一個promise的狀態(tài)和值,并且在resolve或者reject執(zhí)行的時候訪問到,而不是定時器掛起的時候。

  function MyPromise(fn) {
    this.PromiseState = "pending";
    this.PromiseResult = undefined;
    this.callback = {};
    // 保存實例對象的this的值
    const self = this;
    function resolve(data) {
      if (self.PromiseState !== "pending") {
        return;
      }
      // 1.修改對象的狀態(tài)([[promiseState]])
      // 2.設置對象結(jié)果值([[promiseResult]])
      self.PromiseState = "fulfilled";
      self.PromiseResult = data;
      if (self.callback.onResolved) {
        self.callback.onResolved(data);
      }
    }
    function reject(error) {
      if (self.PromiseState !== "pending") {
        return;
      }
      self.PromiseState = "rejected";
      self.PromiseResult = error;
      if (self.callback.onReject) {
        self.callback.onReject(error);
      }
    }
    // 如果throw異常,就需要捕獲到異常然后更改狀態(tài)
    try {
      // 同步調(diào)用執(zhí)行器函數(shù)
      fn(resolve, reject);
    } catch (e) {
      // 修改promise對象的狀態(tài)為失敗
      reject(e);
    }
  }
  MyPromise.prototype.then = function (thenCallback, catchCallback) {
    return new Promise((resolve, reject) => {
      // 調(diào)用回調(diào)函數(shù),要根據(jù)當前的promise實例來調(diào)用
      if (this.PromiseState === "fulfilled") {
        const result = thenCallback(this.PromiseResult);
        resolve(result);
      }
      if (this.PromiseState === "rejected") {
        const result = catchCallback(this.PromiseResult);
        resolve(result);
      }
      // 判斷pending狀態(tài)
      if (this.PromiseState === "pending") {
        // 保存回調(diào)函數(shù)
        this.callback = {
          onResolved: function (data) {
            let result = thenCallback(data);
            resolve(result);
          },
          onRejected: function (error) {
            let result = catchCallback(error);
            resolve(result);
          },
        };
      }
    });
  };
  const p = new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(1);
    }, 1000);
  });
  const p1 = p.then(
    (res) => {
      console.log(res, "成功");
    },
    (err) => {
      console.log(err, "失敗");
    }
  );

  console.log(p, 111);
  console.log(p1, 222);

因為new Promise的實例中遇到了異步的setTimeout會將內(nèi)部的程序掛起,繼續(xù)執(zhí)行.then。

而這時promise實例的狀態(tài)還是pending,因此我們需要對pending的狀態(tài)進行處理,我們主要目的是,在setTimeout的回調(diào)函數(shù)執(zhí)行的時候,能夠執(zhí)行.then的函數(shù)參數(shù)。

這時候就需要進行一個巧妙的設計,我們需要在MyPromise構(gòu)造函數(shù)中增加一個callback的屬性,他是一個對象。

同時我們需要在pending的時候在.then里面將then的兩個參數(shù)傳給callback對象,同時傳入resolve的值或者是reject的值。

在pending的時候我們將函數(shù)傳給了實例的callback,當被掛起的setTimeout回調(diào)函數(shù)執(zhí)行的時候,這時候callback已經(jīng)有值了,所以會根據(jù)resolve或者是reject去執(zhí)行對應的函數(shù),并且將promise的實例的值傳給對應的函數(shù)。

那如我我對p多次調(diào)用.then或者.catch呢?

  const p = new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(1);
    }, 1000);
  });
  const p1 = p.then(
    (res) => {
      console.log(res, "成功");
    },
    (err) => {
      console.log(err, "失敗");
    }
  );
  const p2 = p.then((res) => {
    console.log(res, "成功2");
  });
  console.log(p1, p2);

我們發(fā)現(xiàn)我們的值執(zhí)行了一次,那我們看一下原生的,可以發(fā)現(xiàn)會執(zhí)行2次。

  const p = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(1);
    }, 1000);
  });
  const p1 = p.then(
    (res) => {
      console.log(res, "成功");
    },
    (err) => {
      console.log(err, "失敗");
    }
  );
  const p2 = p.then((res) => {
    console.log(res, "成功2");
  });
  console.log(p1, p2);

原因很簡單,因為我們的callback屬性是一個對象,每次執(zhí)行.then都會覆蓋當前實例原先的callback。

怎么結(jié)局呢?很簡單,我們只要將每次的pending時設置callback改成向數(shù)組加入對象就好。

調(diào)用時我們通過遍歷去調(diào)用就好了。

  function MyPromise(fn) {
    this.PromiseState = "pending";
    this.PromiseResult = undefined;
    this.callbacks = [];
    // 保存實例對象的this的值
    const self = this;
    function resolve(data) {
      if (self.PromiseState !== "pending") {
        return;
      }
      // 1.修改對象的狀態(tài)([[promiseState]])
      // 2.設置對象結(jié)果值([[promiseResult]])
      self.PromiseState = "fulfilled";
      self.PromiseResult = data;
      //調(diào)用成功的回調(diào)函數(shù)
      self.callbacks.forEach((item) => {
        item.onResolved(data);
      });
    }
    function reject(error) {
      if (self.PromiseState !== "pending") {
        return;
      }
      self.PromiseState = "rejected";
      self.PromiseResult = error;
      //執(zhí)行失敗的回調(diào)
      self.callbacks.forEach((item) => {
        item.onRejected(error);
      });
    }
    // 如果throw異常,就需要捕獲到異常然后更改狀態(tài)
    try {
      // 同步調(diào)用執(zhí)行器函數(shù)
      fn(resolve, reject);
    } catch (e) {
      // 修改promise對象的狀態(tài)為失敗
      reject(e);
    }
  }
  MyPromise.prototype.then = function (thenCallback, catchCallback) {
    return new Promise((resolve, reject) => {
      // 調(diào)用回調(diào)函數(shù),要根據(jù)當前的promise實例來調(diào)用
      if (this.PromiseState === "fulfilled") {
        const result = thenCallback(this.PromiseResult);
        resolve(result);
      }
      if (this.PromiseState === "rejected") {
        const result = catchCallback(this.PromiseResult);
        resolve(result);
      }
      // 判斷pending狀態(tài)
      if (this.PromiseState === "pending") {
        // 保存回調(diào)函數(shù)
        this.callbacks.push({
          onResolved: thenCallback,
          onRejected: catchCallback,
        });
      }
    });
  };
  const p = new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(1);
    }, 1000);
  });
  const p1 = p.then(
    (res) => {
      console.log(res, "成功");
    },
    (err) => {
      console.log(err, "失敗");
    }
  );
  const p2 = p.then((res) => {
    console.log(res, "成功2");
  });
  console.log(p1, p2);

然后我們就發(fā)現(xiàn)我們可以正常地調(diào)用了。

那如果.then返回的值是一個promise呢?

這時候我們就需要考慮一下,如果.then返回時一個promise實例或者是一個throw錯誤呢?

  const p = new Promise((resolve, reject) => {
    resolve(1);
  });
  const p1 = p.then(
    (res) => {
      console.log(res, "成功");
      return new Promise((r, j) => {
        r(res);
      });
    },
    (err) => {
      console.log(err, "失敗");
    }
  );
  console.log(p1, "p1");

我們先看一下原生的效果,發(fā)現(xiàn)他時拿到了返回的promise的值。

那是不是就是在調(diào)用then的回調(diào)函數(shù)的時候判斷一下返回的是不是一個promise,如果是promise我們在進行特殊處理。

 // 將.then的判定方式改成如下
      if (this.PromiseState === "fulfilled") {
        try {
          const result = thenCallback(this.PromiseResult);
          if (result instanceof MyPromise) {
            result.then(
              (r) => {
                resolve(r);
              },
              (j) => {
                reject(j);
              }
            );
          } else {
            resolve(result);
          }
        } catch (e) {
          reject(e);
        }
      }
      if (this.PromiseState === "rejected") {
        try {
          const result = catchCallback(this.PromiseResult);
          if (result instanceof MyPromise) {
            result.then(
              (r) => {
                resolve(r);
              },
              (j) => {
                reject(j);
              }
            );
          } else {
            resolve(result);
          }
        } catch (e) {
          reject(e);
        }
      }

如果.then的回掉函數(shù)執(zhí)行后返回的是一個promise實例,但是我們.then默認就返回了一個實例,我們就直接取這個實例的resolve值或者是reject值,將返回值給then默認返回的promise實例。

異步方式同理,但是要注意this的指向問題。

      // 判斷pending狀態(tài)
      if (this.PromiseState === "pending") {
        // 保存回調(diào)函數(shù)
        this.callbacks.push({
          onResolved: function () {
            try {
              // 這里的self指向的是調(diào)用.then的promise實例
              let result = thenCallback(self.PromiseResult);
              //判斷
              if (result instanceof Promise) {
                result.then(
                  (v) => {
                    resolve(v);
                  },
                  (r) => {
                    reject(r);
                  }
                );
              } else {
                resolve(result);
              }
            } catch (e) {
              reject(e);
            }
          },
          onRejected: function () {
            try {
              // 這里的self指向的是調(diào)用.then的promise實例
              let result = catchCallback(self.PromiseResult);
              //判斷
              if (result instanceof Promise) {
                result.then(
                  (v) => {
                    resolve(v);
                  },
                  (r) => {
                    reject(r);
                  }
                );
              } else {
                resolve(result);
              }
            } catch (e) {
              reject(e);
            }
          },
        });
      }
    });

我們發(fā)現(xiàn),代碼已經(jīng)重復了4次,我們就可以進行一個封裝。

  MyPromise.prototype.then = function (thenCallback, catchCallback) {
    const self = this;
    return new Promise((resolve, reject) => {
      function run(type) {
        try {
          //獲取回調(diào)函數(shù)的執(zhí)行結(jié)果
          let result = type(self.PromiseResult);
          //判斷
          if (result instanceof Promise) {
            //如果是 Promise 類型的對象
            result.then(
              (v) => {
                resolve(v);
              },
              (j) => {
                reject(j);
              }
            );
          } else {
            //結(jié)果的對象狀態(tài)為『成功』
            resolve(result);
          }
        } catch (e) {
          reject(e);
        }
      }
      // 調(diào)用回調(diào)函數(shù),要根據(jù)當前的promise實例來調(diào)用
      if (this.PromiseState === "fulfilled") {
        run(thenCallback);
      }
      if (this.PromiseState === "rejected") {
        run(catchCallback);
      }
      // 判斷pending狀態(tài)
      if (this.PromiseState === "pending") {
        // 保存回調(diào)函數(shù)
        this.callbacks.push({
          onResolved: function () {
            run(thenCallback);
          },
          onRejected: function () {
            run(catchCallback);
          },
        });
      }
    });
  };

嗯,封裝完后的一個完整的.then。我們先看一下完整的效果。

  const p1 = p.then(
    (res) => {
      console.log(res, "成功");
      return new Promise((r, j) => {
        r(res);
      });
    },
    (err) => {
      console.log(err, "失敗");
    }
  );
  console.log(p1, "p1");

和原生的基本上是一模一樣了。

接下來就是對catch的實現(xiàn)了

但是我們知道,catch其實就是.then執(zhí)行第二個參數(shù)的一個語法糖。 因此,我們就可以將接收到的回調(diào)函數(shù)直接給.then的第二個參數(shù)。

MyPromise.prototype.catch = function(onRejected){
    return this.then(undefined, onRejected);
}

這時候我們就要注意了,我們給then的第一個參數(shù)賦值了一個undefined。但是我們并沒有對undefined進行處理。而且我們原生的Promise是可以傳入其他數(shù)據(jù)的,不一定是一個回調(diào)函數(shù)。

因此,我們需要在MyPromise的原型里的.then方法進行一個判斷:

    if (typeof catchCallback !== "function") {
      catchCallback = (reason) => {
        throw reason;
      };
    }
    if (typeof thenCallback !== "function") {
      thenCallback = (value) => value;
    }

如果傳入的不是一個函數(shù),我們就將它變成一個函數(shù),并且獲取的是上一個.then返回的promise實例的值。

總體上我們大致完成了,但是Promise還存在構(gòu)造函數(shù)的方法Promise.resolve(value)Promise.reject(error) 這個就很簡單了

//添加 resolve 方法
MyPromise.resolve = function(value){
    //返回promise對象
    return new MyPromise((r, j) => {
        if(value instanceof MyPromise){
            value.then(v=>{
                r(v);
            }, r=>{
                j(r);
            })
        }else{
            //狀態(tài)設置為成功
            resolve(value);
        }
    });
}

判斷resolve傳入的值是否是一個MyPromise實例,如果是,則根據(jù)這個Promise的狀態(tài)來返回,如果不是則直接調(diào)用resolve()

reject同理:

//添加 reject 方法
MyPromise.reject = function(error){
    return new MyPromise((resolve, reject)=>{
        reject(error);
    });
}

Promise.all的實現(xiàn)

Promise.all的實現(xiàn)基于我們原先封裝的MyPromise。

我們先來分析一下原先的Promise.all的方法,它接受一個promise數(shù)組,返回一個新的promise。如果數(shù)組中有一個狀態(tài)是rejected,那將直接返回一個rejected的promise實例。如果都成功了,則返回成功的promise。

  MyPromise.all = function (promiseLists) {
    return new Promise((resolve, reject) => {
      let promiseResults = [];
      let count = 0;
      for (let i = 0; i < promiseLists.length; i++) {
        promiseLists.then(
          (v) => {
            count += 1;
            promiseResults[i] = v;
            if (count === promiseLists.length) {
              resolve(promiseResults);
            }
          },
          (err) => {
            reject(err);
          }
        );
      }
    });
  };

實現(xiàn)上我們定義了一個promiseLists的形參用來接收promise的數(shù)組。因為返回的肯定是一個promise,所以我們直接返回了一個promise的實例。

我們定義了一個count用來計數(shù)成功執(zhí)行的個數(shù),promiseResults則用來接收成功的結(jié)果(要按照順序接收)。最后就是遍歷promiseLists的數(shù)組了,如果是resolve則count+1,并且將值往promiseResults里面塞。如果count的值和接受的數(shù)組長度一樣了,那就是全部的promise返回了fulfilled。如果有一個錯誤,則直接退出,并且將錯誤的promise的值傳給all返回的promise。

到此這篇關(guān)于JavaScript Promise執(zhí)行流程深刻理解的文章就介紹到這了,更多相關(guān)JavaScript Promise內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論