Promise是什么,如何手寫一個符合 PromiseA+規(guī)范的 Promise

什么是 Promise

參考 MDN 定義Promise

cosnt p = new Promise((resolve,reject)=>{})

一個 Promise 必然處于以下幾種狀態(tài)之一:
peding:初始狀態(tài),既沒有被兌現(xiàn),也沒有被拒絕。
fulfilled:意味著操作成功完成。
rejected:意味著操作失敗。

為什么使用Promise

假設有一下代碼,實現(xiàn)每過一秒鐘輸出一句話

function fn1(callback) {
  setTimeout(()=>{
    console.log('fn1')
    callback()
  }, 1000)
}
function fn2(callback) {
  setTimeout(()=>{
    console.log('fn2')
    callback()
  }, 1000)
}
function fn3() {
  setTimeout(()=>{
    console.log('fn3')
  }, 1000)
}
fn1(function(){
  fn2(function(){
    fn3()
  })
})

上面 fn1 里調(diào)用fn2,fn2 里調(diào)用 fn3 這種就形成了回調(diào)地獄,而使用 Promise可以讓代碼變得更優(yōu)雅。下面用 Promise 改造

function fn1(){
  return new Promise((resolve,reject)=>{
    setTeimeout(()=>{
      resolve('fn1')
    },1000)
  })
}
function fn2(){
  return new Promise((resolve,reject)=>{
    setTeimeout(()=>{
      resolve('fn2')
    },1000)
  })
}
function fn3(){
  return new Promise((resolve,reject)=>{
    setTeimeout(()=>{
      resolve('fn3')
    },1000)
  })
}
fn1()
  .then(fn2)
  .then(fn3)

如何手寫一個 Promise

參考Promises/A+規(guī)范

我們先考慮如何使用Promise
可以new 一個promise創(chuàng)建一個實例,接收一個函數(shù)作為參數(shù),這個函數(shù)有兩個參數(shù)分別是resolve,reject

const p = new Promise((resolve, reject) => {
    //成功回調(diào)
    resolve(data)
    //失敗回調(diào)
    reject(reason)
});

有 then 方法,支持鏈式調(diào)用

p.then(console.log('f1'),console.error('f2'))
 .then(console.log('f3'),console.error('f4'))

下面開始手寫一個promise

constructor

我們知道new 一個promise實例時,參數(shù)fn數(shù)是是同步執(zhí)行的

class MyPromise {
  constructor(fn) {
    const resolve = (data) => {};
    const reject = (reason) => {};
    fn.call(undefined, resolve, reject);
  }
  then(onFulfilled, onRejected) {}
}
console.log(1)
const p = new MyPromise((resolve, reject) => {
  console.log("執(zhí)行了");
});
console.log(2)
image.png

一個promise必須處于以下三種狀態(tài)之一:pending(待定)、fulfilled(實現(xiàn))或rejected(拒絕),處于pending狀態(tài)下的promise可以轉為fulfilled或者rejected狀態(tài),一但狀態(tài)改變之后,狀態(tài)就不可變了。這樣在我們的promise中添加上狀態(tài)部分的代碼

class MyPromise {
  //2.1.1
  #status = "pending";
  constructor(fn) {
    const resolve = (data) => {
      //2.1.2.1
      if (this.#status !== "pending") return;
      this.#status = "fulfilled";
    };
    const reject = (reason) => {
      //2.1.3.1
      if (this.#status !== "pending") return;
      this.#status = "rejected";
    };
    fn.call(undefined, resolve, reject);
  }
  then(onFulfilled, onRejected) {}
}

resolve和reject執(zhí)行可以傳遞參數(shù),


image.png

下面添參數(shù)部分的代碼

class MyPromise {
  //規(guī)范 2.1.1
  #status = "pending";
  #result = undefined;
  constructor(fn) {
    const resolve = (data) => {
      //規(guī)范 2.1.2.1
      if (this.#status !== "pending") return;
      this.#status = "fulfilled";
      //2.1.2.2
      this.#result = data;
    };
    const reject = (reason) => {
      //規(guī)范 2.1.3.1
      if (this.#status !== "pending") return;
      this.#status = "rejected";
      //2.1.3.2
      this.#result = reason;
    };
    fn.call(undefined, resolve, reject);
  }
  then(onFulfilled, onRejected) {}
}

還有一種情況,new promise的時候拋出一個錯誤時,promise狀態(tài)變?yōu)閞ejected,結果為拋出的錯誤


image.png

所以調(diào)用fn是需要try catch處理

class MyPromise {
  //規(guī)范 2.1.1
  #status = "pending";
  #result = undefined;
  constructor(fn) {
    const resolve = (data) => {
      //規(guī)范 2.1.2.1
      if (this.#status !== "pending") return;
      this.#status = "fulfilled";
      //2.1.2.2
      this.#result = data;
    };
    const reject = (reason) => {
      //規(guī)范 2.1.3.1
      if (this.#status !== "pending") return;
      this.#status = "rejected";
      //2.1.3.2
      this.#result = reason;
    };
    try {
      fn.call(undefined, resolve, reject);
    } catch (error) {
      reject(error)
    }
  }
  then(onFulfilled, onRejected) {}
}

then

image.png

then接收兩個參數(shù),onFulfilledonRejected,promise狀態(tài)為fulfilled時執(zhí)行onFulfilled回調(diào),狀態(tài)為rejected時執(zhí)行onRejected回調(diào),then方法支持鏈式調(diào)用,所以then返回的還是一個promise

class MyPromise {
  //規(guī)范 2.1.1
  #status = "pending";
  #result = undefined;
  constructor(fn) {
    const resolve = (data) => {
      //規(guī)范 2.1.2.1
      if (this.#status !== "pending") return;
      this.#status = "fulfilled";
      //2.1.2.2
      this.#result = data;
    };
    const reject = (reason) => {
      //規(guī)范 2.1.3.1
      if (this.#status !== "pending") return;
      this.#status = "rejected";
      //2.1.3.2
      this.#result = reason;
    };
    try {
      fn.call(undefined, resolve, reject);
    } catch (error) {
      this.#result = error;
    }
  }
  then(onFulfilled, onRejected) {
    if(this.#status==='fulfilled'){
      onFulfilled(this.#result)
    }
    if(this.#status==='rejected'){
      onRejected(this.#result)
    }
    //2.2.7
    return new MyPromise((resolve, reject) => {});
  }
}

規(guī)范2.2.4 onFulfilled 和 onRejected 只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用,所以在執(zhí)行onFulfilled和onRejected時要用異步調(diào)用

class MyPromise {
  #status = "pending"; //規(guī)范2.1.1  初始狀態(tài)為 pending
  #result = undefined; //返回結果
  //接收一個 fn 函數(shù),
  constructor(fn) {
    const resolve = (data) => {
      //規(guī)范2.1.2  不能轉換為其他任何狀態(tài)
      if (this.#status !== "pending") return;
      this.#status = "fulfilled"; //規(guī)范2.1.2  狀態(tài)變?yōu)?fulfilled
      this.#result = data;
    };
    //規(guī)范 2.1.3.2  有一個失敗原因
    const reject = (reason) => {
      //規(guī)范2.1.3  不能轉換為其他任何狀態(tài)
      if (this.#status !== "pending") return;
      this.#status = "rejected"; //2.1.3    狀態(tài)變?yōu)?rejected
      this.#result = reason;
    };

    // 如果函數(shù)執(zhí)行過程中出錯,狀態(tài)變?yōu)閞ejected
    try {
      fn.call(undefined, resolve, reject);
    } catch (error) {
      this.reject(error);
    }
  }
  //2.2 有一個 then 方法,// 2.2.1 有兩個可選參數(shù)
  then(onFulfilled, onRejected) {
    //2.2.1.1 如果onFulfilled不是一個函數(shù),它必須被忽略
    //2.2.5 onFulfilled和onRejected必須作為函數(shù)被調(diào)用(即沒有this值
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : () => {};
    // 2.2.1.2 如果onRejected不是一個函數(shù),它必須被忽略
    //2.2.5 onFulfilled和onRejected必須作為函數(shù)被調(diào)用(即沒有this值
    onRejected = typeof onRejected === "function" ? onRejected : () => {};


    //2.2.7 必須返回一個 promise
    cosnt p2 = new MyPromise((resolve, reject) => {
      if (this.#status === "fulfilled") {
        //2.2.4 onFulfilled或onRejected不能在執(zhí)行上下文堆棧中只包含平臺代碼之前調(diào)用
        setTimeout(() => {
          onFulfilled(this.#result);
        });
      }
      if (this.#status === "rejected") {
        setTimeout(() => {
          onRejected(this.#result);
        });
      }
    });
  }
  return p2
}
//測試 輸出 1 promise1 4 2
console.log("1");
var p = new MyPromise((resolve, reject) => {
  console.log("promise1");
  resolve(2);
});
p.then(
  (res) => {
    console.log(res)
  },
  () => {
    console.log("reject1");
  }
);
console.log("4");

測試結果看似沒有問題,如果代碼稍微改動一下,就有問題了

console.log("1");
var p = new MyPromise((resolve, reject) => {
  setTimeout(()=>{
     console.log("promise1");
     resolve(2);
  })
});
p.then(
  (res) => {
    console.log(res)
  },
  () => {
    console.log("reject1");
  }
);
console.log("4");

image.png

resolve的結果不輸出了,因為then方法中只對fulfilledrejected的狀態(tài)做了處理,pending狀態(tài)時沒有處理,setTimeout中的代碼是放到異步任務隊列(宏任務)中處理的,運行到p.then時,promise狀態(tài)始終是pending狀態(tài)
下面在then中加入pending狀態(tài)的處理

class MyPromise {
  #status = "pending"; //規(guī)范2.1.1  初始狀態(tài)為 pending
  #result = undefined; //返回結果
  //接收一個 fn 函數(shù),
  constructor(fn) {
    this.queue = [];
    this.state = "pending";
    const resolve = (data) => {
      //規(guī)范2.1.2  不能轉換為其他任何狀態(tài)
      if (this.state !== "pending") return;
      this.state = "fulfilled"; //規(guī)范2.1.2  狀態(tài)變?yōu)?fulfilled
      this.#result = data;
    };
    //規(guī)范 2.1.3.2  有一個失敗原因
    const reject = (reason) => {
      //規(guī)范2.1.3  不能轉換為其他任何狀態(tài)
      if (this.state !== "pending") return;
      this.state = "rejected"; //2.1.3    狀態(tài)變?yōu)?rejected
      this.#result = reason;
    };

    // 如果函數(shù)執(zhí)行過程中出錯,狀態(tài)變?yōu)閞ejected
    try {
      // this.queue.push([resolve, reject]);
      fn.call(undefined, resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  //2.2.1 有一個then方法,有兩個可選參數(shù)
  then(onFulfilled, onRejected) {
    //2.2.1.1 如果onFulfilled不是一個函數(shù),它必須被忽略
    //2.2.5 onFulfilled和onRejected必須作為函數(shù)被調(diào)用(即沒有this值
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : () => {};
    // 2.2.1.2 如果onRejected不是一個函數(shù),它必須被忽略
    //2.2.5 onFulfilled和onRejected必須作為函數(shù)被調(diào)用(即沒有this值
    onRejected = typeof onRejected === "function" ? onRejected : () => {};
    //2.2.7 必須返回一個 promise
    const p2 = new MyPromise((resolve, reject) => {
      //2.2.2.2 在promise實現(xiàn)之前不得調(diào)用它。
      if (this.state === "fulfilled") {
        //2.2.4 onFulfilled或onRejected不能在執(zhí)行上下文堆棧中只包含平臺代碼之前調(diào)用。
        setTimeout((data) => {
          onFulfilled(this.#result);
        });
      }
      //2.2.3.2 在promise被拒絕之前不得調(diào)用它。
      if (this.state === "rejected") {
        //2.2.2.1 它必須在promise實現(xiàn)后調(diào)用,并以promise的值作為其第一個參數(shù)。
        setTimeout((reason) => {
          onRejected(reason);
        });
      }
      //
      if (this.state === "pending") {
        
      }
    });
    return p2;
  }
  #resolvePromise(promise, x) {
    try {
      if (promise === x) {
      }
    } catch (error) {}
  }
}

問題來了,pending狀態(tài)時要做什么呢?
我們知道hen方法中的onFulfilledonRejected執(zhí)行時機一定是等promise狀態(tài)變?yōu)閒ulfilled或者rejected后才執(zhí)行。也就是說要等到resolve或reject執(zhí)行之后再執(zhí)行。很簡單,resolve時調(diào)用onFulfilled就行了,reject時調(diào)用onRejected,
注意then方法支持多次調(diào)用,如何多次調(diào)用呢,很簡單,將onFulfilled和onRejected放入一個隊列中,resolve執(zhí)行時調(diào)用所有成功的回調(diào)onFulfilled,reject執(zhí)行時調(diào)用所有失敗的回調(diào)onRejected

class MyPromise {
  #status = "pending"; //規(guī)范2.1.1  初始狀態(tài)為 pending
  #result = undefined; //返回結果
  //接收一個 fn 函數(shù),
  constructor(fn) {
    this.queue = []; //添加一個隊列,存儲onFulfilled, onRejected
    this.state = "pending";
    const resolve = (data) => {
      console.log('data:',data)
      //規(guī)范2.1.2  不能轉換為其他任何狀態(tài)
      if (this.state !== "pending") return;
      this.state = "fulfilled"; //規(guī)范2.1.2  狀態(tài)變?yōu)?fulfilled
      this.#result = data;
      // 執(zhí)行resolve時再調(diào)用所有onFulfilled
     this.queue.map(callbacks=>{
        const task = this.queue.shift()
        task[0](data)
     })
    };
    //規(guī)范 2.1.3.2  有一個失敗原因
    const reject = (reason) => {
      //規(guī)范2.1.3  不能轉換為其他任何狀態(tài)
      if (this.state !== "pending") return;
      this.state = "rejected"; //2.1.3    狀態(tài)變?yōu)?rejected
      this.#result = reason;
      // 執(zhí)行reject時再調(diào)用所有onRejected
       this.queue.map(callbacks=>{
        const task = this.queue.shift()
        task[1](reason)
     })
    };

    // 如果函數(shù)執(zhí)行過程中出錯,狀態(tài)變?yōu)閞ejected
    try {
      // this.queue.push([resolve, reject]);
      fn.call(undefined, resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  //2.2.1 有一個then方法,有兩個可選參數(shù)
  then(onFulfilled, onRejected) {
    //2.2.1.1 如果onFulfilled不是一個函數(shù),它必須被忽略
    //2.2.5 onFulfilled和onRejected必須作為函數(shù)被調(diào)用(即沒有this值
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : () => {};
    // 2.2.1.2 如果onRejected不是一個函數(shù),它必須被忽略
    //2.2.5 onFulfilled和onRejected必須作為函數(shù)被調(diào)用(即沒有this值
    onRejected = typeof onRejected === "function" ? onRejected : () => {};
    //2.2.7 必須返回一個 promise
    const p2 = new MyPromise((resolve, reject) => {
      //2.2.2.2 在promise實現(xiàn)之前不得調(diào)用它。
      if (this.state === "fulfilled") {
        //2.2.4 onFulfilled或onRejected不能在執(zhí)行上下文堆棧中只包含平臺代碼之前調(diào)用。
        setTimeout((data) => {
          onFulfilled(this.#result);
        });
      }
      //2.2.3.2 在promise被拒絕之前不得調(diào)用它。
      if (this.state === "rejected") {
        //2.2.2.1 它必須在promise實現(xiàn)后調(diào)用,并以promise的值作為其第一個參數(shù)。
        setTimeout((reason) => {
          onRejected(reason);
        });
      }
      //pending狀態(tài)時,將onFulfilled, onRejected存入隊列
      if (this.state === "pending") {
        this.queue.push([onFulfilled, onRejected]);
      }
    });
    return p2;
  }
}

下面測試一下,then方法中的回調(diào)成功執(zhí)行了,但是和正常的promise執(zhí)行順序不太一樣

console.log("1");
var p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    console.log("2");
    resolve(4);
    console.log("3");
  });
});
p.then(
  (res) => {
    console.log('res',res);
  },
  () => {
    console.log("reject1");
  }
);
console.log("5");
image.png

測試正常promise輸出的執(zhí)行結果

console.log("1");
var p = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log("2");
    resolve(4);
    console.log("3");
  });
});
p.then(
  (res) => {
    console.log('res',res);
  },
  () => {
    console.log("reject1");
  }
);
console.log("5");

可以看到resolve的執(zhí)行是在最后執(zhí)行的。如何讓resolve在后面執(zhí)行呢,很簡單放到setTimeout中就行啦


image.png
class MyPromise {
  #status = "pending"; //規(guī)范2.1.1  初始狀態(tài)為 pending
  #result = undefined; //返回結果
  //接收一個 fn 函數(shù),
  constructor(fn) {
    this.queue = []; //添加一個隊列,存儲onFulfilled, onRejected
    const resolve = (data) => {
      //規(guī)范2.1.2  不能轉換為其他任何狀態(tài)
      if (this.#status !== "pending") return;
      setTimeout(() => {
        this.#status = "fulfilled"; //規(guī)范2.1.2  狀態(tài)變?yōu)?fulfilled
        this.#result = data;
        //2.2.6.1 如果/當promise被實現(xiàn)時,所有相應的onFulfilled回調(diào)函數(shù)必須按照它們發(fā)起then調(diào)用的順序執(zhí)行。
        this.queue.map((callbacks) => {
          const task = callbacks[0];
          task(data);
        });
      });
    };
    //規(guī)范 2.1.3.2  有一個失敗原因
    const reject = (reason) => {
      //規(guī)范2.1.3  不能轉換為其他任何狀態(tài)
      if (this.#status !== "pending") return;
      setTimeout(() => {
        this.#status = "rejected"; //2.1.3    狀態(tài)變?yōu)?rejected
        this.#result = reason;
        //2.2.6.2 如果/當promise被拒絕時,所有相應的onRejected回調(diào)函數(shù)必須按照它們發(fā)起then調(diào)用的順序執(zhí)行。
        this.queue.map((callbacks) => {
          const task = callbacks[1];
          task(reason);
        });
      });
    };

    // 如果函數(shù)執(zhí)行過程中出錯,狀態(tài)變?yōu)閞ejected
    try {
      // this.queue.push([resolve, reject]);
      fn(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  //2.2.1 有一個then方法,有兩個可選參數(shù)
  then(onFulfilled, onRejected) {
    //2.2.1.1 如果onFulfilled不是一個函數(shù),它必須被忽略
    //2.2.5 onFulfilled和onRejected必須作為函數(shù)被調(diào)用(即沒有this值
    const onFulfilledcallback =
      typeof onFulfilled === "function" ? onFulfilled : () => {};
    // 2.2.1.2 如果onRejected不是一個函數(shù),它必須被忽略
    //2.2.5 onFulfilled和onRejected必須作為函數(shù)被調(diào)用(即沒有this值
    const onRejectedcallback =
      typeof onRejected === "function" ? onRejected : () => {};
    //2.2.7 必須返回一個 promise
    const p2 = new MyPromise((resolve, reject) => {
    //2.2.2.2 在promise實現(xiàn)之前不得調(diào)用onFulfilled。
    if (this.#status === "fulfilled") {
      //2.2.4 onFulfilled或onRejected不能在執(zhí)行上下文堆棧中只包含平臺代碼之前調(diào)用。
      setTimeout(() => {
        try {
          onFulfilledcallback(this.#result);
        } catch (e) {
          reject(e);
        }
      });
    }
    //2.2.3.2 在promise被拒絕之前不得調(diào)用它。
    if (this.#status === "rejected") {
      //2.2.2.1 它必須在promise實現(xiàn)后調(diào)用,并以promise的值作為其第一個參數(shù)。
      setTimeout(() => {
        try {
          onRejectedcallback(this.#result);
        } catch (e) {
          reject(e);
        }
      });
    }
    //2.2.6then方法可以在同一個promise上多次調(diào)用。
    if (this.#status === "pending") {
      this.queue.push([onFulfilled, onRejected]);
    }
    });
    return p2;
  }
  #resolutionProcedure(promise, x, resolve, reject) {
    try {
      if (promise === x) {
      }
    } catch (error) {}
  }
}

在測試一下

console.log("1");
var p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    console.log("2");
    resolve(4);
    console.log("3");
  });
});
p.then(
  (res) => {
    console.log("resolve", res);
  },
  (reason) => {
    console.log("reject", reason);
  }
);
console.log("5");

結果和正常的promise一樣了


image.png

我們知道then方法中onFulfilled或onRejected可以不是一個函數(shù)

var p = new Promise((resolve,reject)=>{
  resolve(1)
})
p.then(undefined,reason=>{
  console.log(reason)
}).then((res)=>{
  console.log(res)
},undefined)

需要再then方法中處理一下resolvereject的返回值

class MyPromise {
  ...
  //2.2.1 有一個then方法,有兩個可選參數(shù)
  then(onFulfilled, onRejected) {
    //2.2.1.1 如果onFulfilled不是一個函數(shù),它必須被忽略
    //2.2.5 onFulfilled和onRejected必須作為函數(shù)被調(diào)用(即沒有this值

    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (data) => data;
    // 2.2.1.2 如果onRejected不是一個函數(shù),它必須被忽略
    //2.2.5 onFulfilled和onRejected必須作為函數(shù)被調(diào)用(即沒有this值

    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (reason) => {
            throw reason;
          };
    //2.2.7 必須返回一個 promise
    const p2 = new MyPromise((resolve, reject) => {
      //2.2.2.2 在promise實現(xiàn)之前不得調(diào)用onFulfilled。
      if (this.#status === "fulfilled") {
        //2.2.4 onFulfilled或onRejected不能在執(zhí)行上下文堆棧中只包含平臺代碼之前調(diào)用。
        setTimeout(() => {
          try {
            const x = onFulfilled(this.#result);
            this.#resolvePromise(p2, x, resolve, reject);
          } catch (e) {
            //2.2.7.2 如果onFulfilled或onRejected拋出異常e,則promise2必須以e作為原因被拒絕
            reject(e);
          }
        });
      }
      //2.2.3.2 在promise被拒絕之前不得調(diào)用它。
      if (this.#status === "rejected") {
        //2.2.2.1 它必須在promise實現(xiàn)后調(diào)用,并以promise的值作為其第一個參數(shù)。
        setTimeout(() => {
          try {
            const x = onRejected(this.#result);
            this.#resolvePromise(p2, x, resolve, reject);
          } catch (e) {
            //2.2.7.2 如果onFulfilled或onRejected拋出異常e,則promise2必須以e作為原因被拒絕
            reject(e);
          }
        });
      }
      //2.2.6then方法可以在同一個promise上多次調(diào)用。
      if (this.#status === "pending") {
        this.queue.push([
          () => {
            setTimeout(() => {
              try {
                let x = onFulfilled(this.#result);
                this.#resolvePromise(p2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            });
          },
          () => {
            setTimeout(() => {
              try {
                let x = onRejected(this.#result);
                this.#resolvePromise(p2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            });
          },
        ]);
      }
    });
    return p2;
  }
  /**
   * 對resolve()、reject() 進行改造增強 針對resolve()和reject()中不同值情況 進行處理
   * @param  {promise} promise2 promise1.then方法返回的新的promise對象
   * @param  {[type]} x         promise1中onFulfilled或onRejected的返回值
   * @param  {[type]} resolve   promise2的resolve方法
   * @param  {[type]} reject    promise2的reject方法
   */
  #resolvePromise(promise2, x, resolve, reject) {
    //2.3.1 如果promise和x引用同一個對象,則以TypeError為原因拒絕promise
    if (x === promise2) {
      return reject(new TypeError("Chaining cycle detected for promise"));
    }
    //2.3.2 如果x是一個promise,采用其狀態(tài)
    if (x instanceof MyPromise) {
      if (x.#status === "pending") {
        x.then(
          (y) => {
            this.#resolvePromise(promise2, y, resolve, reject);
          },
          () => {
            reject(x.#result);
          }
        );
      }
      if (x.#status === "fulfilled") {
        resolve(x.#result);
      }
      if (x.#status === "rejected") {
        reject(x.#result);
      }
    }
    //2.3.3 否則,如果x是一個對象或函數(shù):
    //注意x不能為null
    else if (x !== null && (x instanceof Object || typeof x === "function")) {
      try {
        //2.3.3.2
        var then = x.then;
      } catch (e) {
        //2.3.3.3
        reject(e);
      }
      if (typeof then === "function") {
        //定義called表示y,r是否被調(diào)用過
        let called = false;
        //2.3.3.3.4
        try {
          then.call(
            x,
            //2.3.3.3.1
            (y) => {
              //2.3.3.3.3
              if (called) return;
              this.#resolvePromise(p2, y, resolve, reject);
              called = true;
            },
            //2.3.3.3.2
            (r) => {
              //2.3.3.3.3
              if (called) return;
              reject(r);
              called = true;
            }
          );
        } catch (e) {
          //2.3.3.3.4.1
          if (called) return;
          //2.3.3.3.4.2
          reject(e);
        }
      } else {
        resolve(x);
      }
    }
    //2.3.4 如果x不是對象或函數(shù),則用x來實現(xiàn)promise。
    else {
      resolve(x);
    }
    try {
    } catch (error) {}
  }
}

實現(xiàn)鏈式調(diào)用

上面代碼中then方法返回一個promsie,那么onFulFilled和onReject的結果如何處理呢?
參考promise a+規(guī)范2.2.7

  • 2.2.7.1如果onFulfilled或onRejected返回一個值x,則運行Promise Resolution Procedure [[Resolve]](promise2, x)。
  • 2.2.7.2 如果onFulfilled或onRejected拋出異常e,則promise2必須以e作為原因被拒絕。
  • 2.2.7.3 如果onFulfilled不是一個函數(shù)且promise1被實現(xiàn),則promise2必須以與promise1相同的值被實現(xiàn)。
  • 2.2.7.4 如果onRejected不是一個函數(shù)且promise1被拒絕,則promise2必須以與promise1相同的原因被拒絕。
    這里我們定義個函數(shù)resolvePromise(promise,x resolve,reject) 來處理x,在then方法中分別在不同狀態(tài)下用resolvePromise方法處理
class MyPromise {
 ...
  //2.2.1 有一個then方法,有兩個可選參數(shù)
  then(onFulfilled, onRejected) {
    //2.2.1.1 如果onFulfilled不是一個函數(shù),它必須被忽略
    //2.2.5 onFulfilled和onRejected必須作為函數(shù)被調(diào)用(即沒有this值

    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : () => {};
    // 2.2.1.2 如果onRejected不是一個函數(shù),它必須被忽略
    //2.2.5 onFulfilled和onRejected必須作為函數(shù)被調(diào)用(即沒有this值

    onRejected = typeof onRejected === "function" ? onRejected : () => {};
    //2.2.7 必須返回一個 promise
    const p2 = new MyPromise((resolve, reject) => {
      //2.2.2.2 在promise實現(xiàn)之前不得調(diào)用onFulfilled。
      if (this.#status === "fulfilled") {
        //2.2.4 onFulfilled或onRejected不能在執(zhí)行上下文堆棧中只包含平臺代碼之前調(diào)用。
        setTimeout(() => {
          try {
            const x = onFulfilled(this.#result);
            this.#resolvePromise(p2, x, resolve, reject);
          } catch (e) {
            //2.2.7.2 如果onFulfilled或onRejected拋出異常e,則promise2必須以e作為原因被拒絕
            reject(e);
          }
        });
      }
      //2.2.3.2 在promise被拒絕之前不得調(diào)用它。
      if (this.#status === "rejected") {
        //2.2.2.1 它必須在promise實現(xiàn)后調(diào)用,并以promise的值作為其第一個參數(shù)。
        setTimeout(() => {
          try {
            const x = onRejected(this.#result);
            this.#resolvePromise(p2, x, resolve, reject);
          } catch (e) {
            //2.2.7.2 如果onFulfilled或onRejected拋出異常e,則promise2必須以e作為原因被拒絕
            reject(e);
          }
        });
      }
      //2.2.6then方法可以在同一個promise上多次調(diào)用。
      if (this.#status === "pending") {
        this.queue.push([
          () => {
            setTimeout(() => {
              try {
                let x = onFulfilled(this.#result);
                this.#resolvePromise(p2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            });
          },
          () => {
            setTimeout(() => {
              try {
                let x = onRejected(this.#result);
                this.#resolvePromise(p2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            });
          },
        ]);
      }
    });
    return p2;
  }
  /**
   * 對resolve()、reject() 進行改造增強 針對resolve()和reject()中不同值情況 進行處理
   * @param  {promise} promise2 promise1.then方法返回的新的promise對象
   * @param  {[type]} x         promise1中onFulfilled或onRejected的返回值
   * @param  {[type]} resolve   promise2的resolve方法
   * @param  {[type]} reject    promise2的reject方法
   */
  #resolvePromise(promise2, x, resolve, reject) {
    
  }
}

按照下面規(guī)范實現(xiàn)resolvePromise


image.png
/**
   * 對resolve()、reject() 進行改造增強 針對resolve()和reject()中不同值情況 進行處理
   * @param  {promise} promise2 promise1.then方法返回的新的promise對象
   * @param  {[type]} x         promise1中onFulfilled或onRejected的返回值
   * @param  {[type]} resolve   promise2的resolve方法
   * @param  {[type]} reject    promise2的reject方法
   */
#resolvePromise(promise2, x, resolve, reject) {
    //2.3.1 如果promise和x引用同一個對象,則以TypeError為原因拒絕promise
    if (x === promise2) {
      return reject(new TypeError("Chaining cycle detected for promise"));
    }
    //2.3.2 如果x是一個promise,采用其狀態(tài)
    if (x instanceof MyPromise) {
      if (x.#status === "pending") {
        x.then(
          (y) => {
            this.#resolvePromise(promise2, y, resolve, reject);
          },
          () => {
            reject(x.#result);
          }
        );
      }
      if (x.#status === "fulfilled") {
        resolve(x.#result);
      }
      if (x.#status === "rejected") {
        reject(x.#result);
      }
    }
    //2.3.3 否則,如果x是一個對象或函數(shù):
    //注意x不能為null
    else if (x !== null && (typeof x === "object" || typeof x === "function")) {
      var then;
      try {
        //2.3.3.2
        then = x.then;
        if (typeof then === "function") {
          //定義called表示y,r是否被調(diào)用過
          let called = false;
          //2.3.3.3.4
          try {
            then.call(
              x,
              //2.3.3.3.1
              (y) => {
                //2.3.3.3.3
                if (called) return;
                called = true;
                this.#resolvePromise(promise2, y, resolve, reject);
              },
              //2.3.3.3.2
              (r) => {
                //2.3.3.3.3
                if (called) return;
                called = true;
                reject(r);
              }
            );
          } catch (e) {
            //2.3.3.3.4.1
            if (called) return;
            //2.3.3.3.4.2
            reject(e);
          }
        } else {
          resolve(x);
        }
      } catch (e) {
        //2.3.3.3
        reject(e);
      }
    }
    //2.3.4 如果x不是對象或函數(shù),則用x來實現(xiàn)promise。
    else {
      resolve(x);
    }
  }

下面測試一下

console.log("1");
var p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    console.log("2");
    resolve(4);
    console.log("3");
  });
});
p.then(
  (res) => {
    console.log("resolve1", res);
    return new MyPromise((resolve, reject) => {
      resolve(123);
    });
  },
  (reason) => {
    console.log("reject1", reason);
  }
).then(
  (res) => {
    console.log("resolve2", res);
  },
  (reason) => {
    console.log("reject2", reason);
  }
);
console.log("5");

可以看到結果和替換成官方Promise的結果一致


image.png

image.png
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容