1. 程式人生 > 其它 >Promise練習三(手寫簡版promise + 符合promise/A+規範的promise)

Promise練習三(手寫簡版promise + 符合promise/A+規範的promise)

技術標籤:好好學習javascript

JavaScript實現一個簡易的Promise物件

class MyPromise {
  constructor(handleFn) {
    // Promise的狀態
    this.PromiseState = "pending";
    // Promise的結果
    this.PromiseValue = null;

    // 畫重點!!!這兩個list是為了將下一個Promise與當前Promise相關聯
    this._toNextResolve = [];
    this._toNextReject =
[]; // Promise接收的函式引數 handleFn(this.triggerResolve.bind(this), this.triggerReject.bind(this)); } // resolve處理 triggerResolve(data) { // 這裡使用setTimeout的原因是:確保resolve處理函式在handleFn所處的事件迴圈後以新的堆疊非同步執行。(參見:2.4) setTimeout(() => { // Promise的狀態不是pending時,不能被修改狀態。(參見:1) if (this
.PromiseState !== "pending") return; // 修改Promise的狀態和值 this.PromiseState = "fullfilled"; this.PromiseValue = data; this._toNextResolve.forEach(item => item(data)); this._toNextResolve = []; }, 0); } // reject處理,同上 triggerReject(data) { setTimeout
(() => { if (this.PromiseState !== "pending") return; this.PromiseState = "rejected"; this.PromiseValue = data; this._toNextReject.forEach(item => item(data)); this._toNextReject = []; }, 0); } // 將多個Promise包裝成一個新的Promise例項,並在所有非同步操作執行完後執行回撥 static all(list) { const result = []; let count = 0; return new MyPromise(function(resolve, reject) { for (let [i, MyPromiseInstance] of list.entries()) { MyPromiseInstance.then( res => { result[i] = res; count++; if (count === list.length) resolve(result); }, err => { reject(err); } ); } }); } static race(list) { return new MyPromise(function(resolve, reject) { for (let [i, MyPromiseInstance] of list.entries()) { MyPromiseInstance.then( res => { resolve(res); }, err => { reject(err); } ); } }); } // static resolve(val) { // return new MyPromise(resolve=>resolve(val)); // } // static reject(val) { // return new MyPromise(resolve=>{}, reject=>reject(val)); // } } MyPromise.prototype.then = function(onResolved, onRejected) { const { PromiseState, PromiseValue } = this; // then方法必須返回一個Promise物件。(參見:2.7) return new MyPromise((onNextResolved, onNextRejected) => { function onLastFullFilled(val) { if (typeof onResolved !== "function") { // 如果onResolved不是函式,則需要忽略他。(參見:2.1) onNextResolved(val); } else { const res = onResolved(val); if (res && typeof res.then === "function") { // 若res 是一個 promise res.then(onNextResolved); } else { // 若res 非 promise,則直接執行下一個 onNextResolved onNextResolved(res); } } } function onLastRejected(val) { if (typeof onRejected !== "function") { // 如果onRejected不是函式,則需要忽略他。(參見:2.1) onNextRejected(val); } else { const res = onRejected(val); if (res && typeof res.then === "function") { // 若res 是一個 promise res.then(onNextRejected); } else { // 若res 非 promise,則直接執行下一個 onNextRejected onNextRejected(res); } } } switch (PromiseState) { case "pending": { this._toNextResolve.push(onLastFullFilled); this._toNextReject.push(onLastRejected); break; } } }); }; const p1 = function() { return new MyPromise(function(resolve, reject) { setTimeout(function() { const random = Math.random() * 10; if (random > 5) { resolve("已完成1:" + random); } else { reject("已拒絕1:" + random); } }, 1000); }); }; const p2 = function() { return new MyPromise(function(resolve, reject) { const random = Math.random() * 10; resolve("已完成2:" + random); }); }; const p3 = function() { return new MyPromise(function(resolve, reject) { const random = Math.random() * 10; resolve("已完成3:" + random); }); }; p1().then( res => { console.log('then方法:'); console.log(res); }, err => { console.log('then方法:'); console.log(err); } ); MyPromise.all([p1(), p2(), p3()]).then( res => { console.log('all方法:'); console.log(res); }, err => { console.log('all方法:'); console.log(err); } ); MyPromise.race([p1(), p2(), p3()]).then( res => { console.log('race方法:'); console.log(res); }, err => { console.log('race方法:'); console.log(err); } );

按照Promise/A+規範,使用typescript實現Promise

  // Promise/A+ 1.3: "value" is any legal JavaScript Value.
  type PromiseValue = any;
  // Promise/A+ 1.5: "reason" is a  value that indicates why a promise was rejected.
  type PromiseReason = any;
  // Promise/A+ 2.2.1: Both onFulfilled and onRejected are optional arguments.
  type onFulfilled = any;
  type onRejected = any;
  // Promise/A+ 2.1: A Promise must be in one of three states:  pending, fulfilled, or rejected.
  enum PromiseState {
    Pending = "pending",
    Fulfilled = "fulfilled",
    Rejected = "rejected"
  }
  type resolve = (value: PromiseValue) => void;
  type reject = (reason: any) => void;
  type executor = (resolve: resolve, reject: reject) => void;

  // 型別判斷
  const ToString = Object.prototype.toString;
  const IsFunctionType = (val: any): boolean => ToString.call(val) === "[object Function]";
  const IsObjectType = (val: any): boolean => ToString.call(val) === "[object Object]";
  const IsNullType = (val: any): boolean => ToString.call(val) === "[object Null]";
  const IsPromiseType = (val: any): boolean => val instanceof PromiseImplement;
  const hasAttribute = (target: any, name: string): boolean => name in target;

  const promiseProcedure = function(promise: PromiseImplement, x: any, resolve: resolve, reject: reject){
    // Promise/A+ 2.3.1: If promise and x refer to the same object, reject promise with a TypeError as the reason.
    if(promise == x){
      const err = new TypeError('Endless loop detected for Promise');
      return reject(err);
    }
    // Promise/A+ 2.3.2: If x is a promise, adopt its state
    if(IsPromiseType(x)){
      if(x.state == PromiseState.Pending){
        // Promise/A+ 2.3.2.1: If x is pending, promise must remain pending until x is fullfilled or rejected.
        x.then((val: PromiseValue) => promiseProcedure(promise, val, resolve, reject), reject);
      }else{
        // Promise/A+ 2.3.2.2: If/when x is fulfilled, fulfill promise with the same value.
        // Promise/A+ 2.3.2.3 If/when x is rejected, reject promise with the same reason.
        x.then(resolve, reject);
      }
    }else if(IsObjectType(x) || IsFunctionType(x)){
      let ignored: boolean = false;
      // Promise/A+ 2.3.3: If x is an Object or funciton
      try{
        // Promise/A+ 2.3.3.1: let then be x.then.
        let then = x.then;      
        if(IsFunctionType(then)){
          const resolvePromise = (y: any) => {
            // Promise/A+ 2.3.3.3.1: If/when resolvePromise is called with a value y, run[[Resolve]](promise, y)
            if(ignored) return;
            ignored = true;
            promiseProcedure(x, y, resolve, reject);
          }
          const rejectPromise = (r: any) => {
            // Promise/A+ 2.3.3.3.2: If/when rejectPromise is called with a reason r, reject promise with r.
            if(ignored) return;
            ignored = true;
            return reject(r);
          }
          // Promise/A+ 2.3.3.3: If then is a function,call it with x as this, first argument resolvePromise, and second argument rejectPromsie
          then(resolvePromise, rejectPromise).bind(x);
        }else {
          // Promise/A+ 2.3.3.4: If then is not a function, fulfill promise with x.
          resolve(x);
        }
      }catch(e){
        if(ignored) return;
        // Promise/A+ 2.3.3.2: if retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
        reject(e);
      }
    }else {
      // Promise/A+ 2.3.4: If x is not an object or function, fulfill promise with x
      resolve(x);
    }
  }

  class PromiseImplement {
    state: PromiseState;
    value: PromiseValue;
    reason: PromiseReason;
    onFulfilledCallbacks: onFulfilled[] = [];
    onRejectedCallbacks: onRejected[] = [];

    constructor(executor: executor) {
      this.state = PromiseState.Pending;

      if (!IsFunctionType(executor)) {
        throw new TypeError(`Promise resolver ${executor} is not a function`);
      }
      if (!(this instanceof PromiseImplement)) {
        throw new TypeError("undefined is not a promise");
      }
      executor(this.triggerResolve.bind(this), this.triggerReject.bind(this));
    }
    triggerResolve(val) {
      setTimeout(()=>{
        if(this.state !== PromiseState.Pending) return;
        this.state = PromiseState.Fulfilled;
        this.value = val;
        this.onFulfilledCallbacks.forEach((onFulfilled: onFulfilled)=>onFulfilled());
        this.onFulfilledCallbacks = [];
      }, 0);
    }
    triggerReject(reason) {
      setTimeout(()=>{
        if(this.state !== PromiseState.Pending) return;
        this.state = PromiseState.Rejected;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((onRejected: onRejected)=>onRejected());
        this.onRejectedCallbacks = [];
      },0)
    }

    // Promise/A+ 2.2: A promise must provide a then method to access its current or eventual valur or reason.
    // Promise/A+ 2.2: A promise's then method accepts two arguments: onFulfilled, onRejected
    then(onFulfilled?: onFulfilled, onRejected?: onRejected) {
      // Promise/A+ 2.2.1.1: If onFulfilled is not a function, it must be ignored.
      // Promise/A+ 2.2.5: onFulfilled must be called as function
      !IsFunctionType(onFulfilled) &&
        (onFulfilled = (PromiseValue: PromiseValue) => PromiseValue);
      // Promise/A+ 2.2.1.2: If onRejected is not a function,it must be ignored.
      // Promise/A+ 2.2.5: onRejected must be called as function
      !IsFunctionType(onRejected) &&
        (onRejected = (PromiseReason: PromiseReason) => PromiseReason);

      // Promise/A+ 2.2.7: then must return a promise.
      const promise2 = new PromiseImplement((resolve, reject)=>{
        if(this.state == PromiseState.Pending){
          // Promise/A+ 2.2.2: If onFulfilled is a function, it must be called after promise is fulfilled,with promise's value as its first argument.
          this.onFulfilledCallbacks.push(()=>{
            const x = onFulfilled(this.value);
            promiseProcedure(this, x, resolve, reject);
          });
          // Promise/A+ 2.2.3: If onRejected is a function, it must be called after promise is rejected,with promise's reason as its first argument.
          this.onRejectedCallbacks.push(()=>{
            const x = onRejected(this.reason);
            promiseProcedure(this, x, resolve, reject);
          });
        }
      });
      return promise2;
    }
  }

  const p1 = function() {
    return new PromiseImplement(function(resolve, reject) {
      setTimeout(function() {
        const random = Math.random() * 10;
        if (random > 5) {
          resolve("已完成1:" + random);
        } else {
          reject("已拒絕1:" + random);
        }
      }, 1000);
    });
  };
  p1().then((res)=>{
    console.log(res)
  }, (err)=>{
    console.log(err);
  }).then((res)=>{
    console.log('完成2:' + res);
  },(err)=>{
    console.log('拒絕3:' + err);
  });