Skip to content
Go back

Promise简易实现

《Promise必知必会》一文里,我介绍了Promise的基础知识和常用方法。虽然在日常开发中,这些内容已经满足二八定律,然而在面试中,写手实现一个Promise的问题,依旧出现在考官的高频题库里。

那些口口声声 “老子学不动”的人 应该看着你们 像我一样 前端积攒了几十年的技术 所有的JavaScript、CSS、HTML和框架源码 像是专门为你们准备的面试礼物

现代Promise都是基于Promises/A+的标准实现的,本文由于是对Promise的简易实现,因此对于标准里的一些特殊情况,暂不考虑。

Promise雏形

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok')
  }, 1000);
});

p.then((res) => {
  console.log('res1', res);
  return 'ok2';
}).then((res) => {
  console.log('res2', res);
})

从上面这段原生Promise代码可以看出,如果我们需要自己实现一个Promise,需要解决以下几个问题:

  1. Promise构造函数的实现
  2. resolvereject两个参数是由JS引擎内部提供的,现在需要我们自己代码实现
  3. then方法的链式调用
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  constructor(executor) {
    this.status = PENDING;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    this.value = '';
    this.reason = '';

    try {
      // executor是同步执行(new时立刻执行)
      executor(this._resolve.bind(this), this._reject.bind(this));
    } catch (e) {
      this._reject(e);
    }
  }

  _resolve(value) {
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.value = value;
      this.onFulfilledCallbacks.forEach(fn => fn());
    }
  }

  _reject(reason) {
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = reason;
      this.onRejectedCallbacks.forEach(fn => fn());
    }
  }

  then(onFulfilled, onRejected) {
    // then方法都是异步的
    // setTimeout模拟微任务(node端也可以用process.nextTick模拟)

    if (this.status === FULFILLED) {
      setTimeout(() => {
        onFulfilled(this.value);
      })
    }

    if (this.status === REJECTED) {
      setTimeout(() => {
        onRejected(this.reason);
      })
    }

    if (this.status === PENDING) {
      this.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          onFulfilled(this.value);
        })
      })

      this.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          onRejected(this.reason);
        })
      })

      return this;  // 没啥用
    }
  }

}

上述代码是最简易版的Promise,面试时请务必答出

测试一下


// 同步resovle
const p1 = new MyPromise((resolve, reject) => {
  resolve('ok');
});

p1.then((res) => {
  console.log('res1', res);
  return 'ok2';
})

// 异步resovle
const p2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok')
  }, 1000);
});

p2.then((res) => {
  console.log('res1', res);
  return 'ok2';
})

then链式调用

说到链式调用,首先想到的肯定是return this。如果我们简单粗暴的在then方法的最后return this

const p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok')
  }, 1000);
});

p.then((res) => {
  // ok
  console.log('res1', res);
  // ok2无法传递给下一个then方法
  return 'ok2';
}).then((res) => {
  // ok
  console.log('res2', res);
})

虽然链式调用实现了,但是不同的then方法获取到的res对象,永远是同一个值("ok")。因此,then方法需要返回的是一个全新的Promise

class MyPromise {
  // 其他代码省略
  then(onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolve(x);
          } catch (e) {
            reject(e);
          }
        })
      }

      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            // 注意这里是resolve,而不是reject!
            resolve(x);
          } catch (e) {
            reject(e);
          }
        })
      }

      if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolve(x);
            } catch (e) {
              reject(e);
            }
          })
        })

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolve(x);
            } catch (e) {
              reject(e);
            }
          })
        })
      }
    });

    return promise2;
  }
}

这是最难理解的一部分!

必须承认:在很长一段时间内,我也没看懂这段代码

我们先来理清楚几个要点:

const p1 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok')
  }, 1000);
});

const handle1 = (res) => {
  // ok
  console.log('res1', res);
  // ok2现在能传递给下一个then方法
  return 'ok2';
};

const p2 = p1.then(handle1);

const handle2 = (res) => {
  // ok2
  console.log('res2', res);
};

const p3 = p2.then(handle2)
  1. p1Promise对象,通过new MyPromise得到
  2. p2也是Promise对象,通过p1.then(handle1)返回
  3. p3也是Promise对象,通过p2.then(handle2)返回
  4. 每个Promise对象都有自己的resolve方法,调用它,当前promise对象立刻改变状态,同时开始执行它内部注册的回调函数onFulfilledCallbacks
  5. p2的状态什么时候改变,由handle1决定(问题:handle1里面没有p2的resolve方法,它是怎么改变p2的状态呢? 答案见下面注释)
  6. p3的状态什么时候改变,由handle2决定

搞清楚了上面几个概念,我们现在来再看看then方法的实现:

class MyPromise {
  then(onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject) => {
      // this指的是前一个promise,不是当前的promise2

      // this.status指的是前一个promise的状态,不是promise2的状态
      if (this.status === PENDING) {
        // this.onFulfilledCallbacks指的是前一个promise的onFulfilledCallbacks
        // 没有直接把onFulfilled塞进onFulfilledCallbacks里,而是包装了一个匿名函数fn,这个fn函数是个闭包,记住了onFulfilled和resolve两个函数
        // onFulfilled是前一个promise then方法注册的回调函数
        // resolve是当前promise2的resovle
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);

              // *******【此处就是要点5的答案!!!】*********
              // 调用resolve才能改变promise2的状态,才能执行promise2里的onFulfilledCallbacks
              resolve(x);
            } catch (e) {
              reject(e);
            }
          })
        })
      }
    });

    return promise2;
  }
}

看完注释,如果你还是很绕,可以通过断点调试理清流程。如果还是很懵,不建议继续阅读之后内容

强烈建议反复体会这段代码(点击展开) ```javascript const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected';

class MyPromise { constructor(executor) { this.status = PENDING; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; this.value = ”; this.reason = ”;

try {
  executor(this._resolve.bind(this), this._reject.bind(this));
} catch (e) {
  this.reject(e);
}

}

_resolve(value) { if (this.status === PENDING) { this.status = FULFILLED; this.value = value; this.onFulfilledCallbacks.forEach(fn => fn()); } }

_reject(reason) { if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach(fn => fn()); } }

then(onFulfilled, onRejected) { const promise2 = new MyPromise((resolve, reject) => { if (this.status === FULFILLED) { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject);

      } catch (e) {
        reject(e);
      }
    })
  }

  if (this.status === REJECTED) {
    setTimeout(() => {
      try {
        let x = onRejected(this.reason);
        resolvePromise(promise2, x, resolve, reject);
      } catch (e) {
        reject(e);
      }
    })
  }

  if (this.status === PENDING) {
    this.onFulfilledCallbacks.push(() => {
      setTimeout(() => {
        try {
          let x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      })
    })

    this.onRejectedCallbacks.push(() => {
      setTimeout(() => {
        try {
          let x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      })
    })
  }
});

return promise2;

}

}

const p = new MyPromise((resolve, reject) => { setTimeout(() => { resolve(‘ok’) }, 1000); });

p.then((res) => { console.log(‘res1’, res); return ‘ok2’; }).then((res) => { console.log(‘res2’, res); })

</details>

### 支持返回Promise
前面的代码还是有些问题,如果在`then`方法的成功回调函数里,返回的不是普通对象,而是一个`Promise`对象,我们的代码就无法正常运行了:
```javascript
const p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok1')
  }, 1000);
});

p.then((res) => {
  console.log('res1', res);
  // 返回一个新Promise
  return new MyPromise((resolve) => {
    setTimeout(() => {
      resolve('ok2')
    }, 1000);
  });
}).then((res) => {
  // 立刻执行,打印的是Promise对象,而不是ok2
  console.log('res2', res);
})

出错的原因

// x可能是Promise对象,而我们直接resovle了这个Promise对象
let x = onFulfilled(this.value);
resolve(x);

正确的做法

let x = onFulfilled(this.value);
if (x instanceof MyPromise) {
  // 如果x是Promise对象,则把resolve传递给x.then方法,resovle何时调用让x做决定
  x.then(resolve, reject)
} else {
  resovle(x)
}

修改后的then方法

then(onFulfilled, onRejected) {
  const promise2 = new MyPromise((resolve, reject) => {
    if (this.status === FULFILLED) {
      setTimeout(() => {
        try {
          let x = onFulfilled(this.value);
          if (x instanceof MyPromise) {
            x.then(resolve, reject);
          } else {
            resolve(x);
          }

        } catch (e) {
          reject(e);
        }
      })
    }

    if (this.status === REJECTED) {
      setTimeout(() => {
        try {
          let x = onRejected(this.reason);
          if (x instanceof MyPromise) {
            x.then(resolve, reject);
          } else {
            resolve(x);
          }
        } catch (e) {
          reject(e);
        }
      })
    }

    if (this.status === PENDING) {
      this.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            if (x instanceof MyPromise) {
              x.then(resolve, reject);
            } else {
              resolve(x);
            }
          } catch (e) {
            reject(e);
          }
        })
      })

      this.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            if (x instanceof MyPromise) {
              x.then(resolve, reject);
            } else {
              resolve(x);
            }
          } catch (e) {
            reject(e);
          }
        })
      })
    }
  });

  return promise2;
}

支持返回thenable对象

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok1')
  }, 1000);
});

p.then((res) => {
  console.log('res1', res);
  // 返回一个thenable对象
  return {
    then: function(resolve, reject) {
      setTimeout(() => {
        resolve('ok2')
      }, 1000);
    }
  }
}).then((res) => {
  console.log('res2', res);
})

Promise还没有被Promises/A+规范之前,有许多类库实现了自己的Promise,比如jQuerydeferred对象。为了实现类Promise的兼容,只要一个对象具有then方法,那它就可以被Promise识别。

const thenable =  {
  then: function (resolve, reject) {
    setTimeout(() => {
      resolve('ok2')
    }, 1000);
  }
}

thenable对象的作用:使promise的实现更具有通用性 识别thenable或行为类似Promise对象可以根据其是否具有then()方法来判断(鸭子类型)

function isThenable(obj) {
  return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}
// 为了判断x是普通对象还是thenable对象(Promise对象也属于thenable对象),我们可以抽离出一个通用方法resolvePromise
let x = onFulfilled(this.reason);
resolvePromise(promise2, x, resolve, reject);
function resolvePromise(promise2, x, resolve, reject) {
  // Promise对象或者thenable对象
  if (x && (typeof x === 'object' || typeof x === 'function') && typeof x.then === 'function') {
    try {
      x.then(resolve, reject)
    } catch (e) {
      reject(e);
    }
  } else {
    // 其他普通对象
    resolve(x);
  }
}

较完整的Promise

较上文有部分修改,比如添加了then方法参数的默认值,循环引用的判断等

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function isThenable(obj) {
  return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}

class MyPromise {
  constructor(executor) {
    this.status = PENDING;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    this.value = '';
    this.reason = '';

    try {
      executor(this._resolve.bind(this), this._reject.bind(this));
    } catch (e) {
      this._reject(e);
    }
  }

  _resolve(value) {
    // Promise对象或者thenable对象
    if (isThenable(value)) {
      return value.then(this._resolve.bind(this), this._reject.bind(this))
    }
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.value = value;
      this.onFulfilledCallbacks.forEach(fn => fn());
    }
  }

  _reject(reason) {
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = reason;
      this.onRejectedCallbacks.forEach(fn => fn());
    }
  }

  then(onFulfilled, onRejected) {
    // onFulfilled onRejected 提供默认值
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);

          } catch (e) {
            reject(e);
          }
        })
      }

      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        })
      }

      if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          })
        })

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          })
        })
      }
    });

    return promise2;
  }

}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    reject(new TypeError('循环引用'));
  }

  // Promise对象或者thenable对象
  if (isThenable(x)) {
    try {
      x.then(resolve, reject)
    } catch (e) {
      reject(e);
    }
  } else {
    // 其他普通对象
    resolve(x);
  }
}

测试一下

const p = new MyPromise((resolve) => {
  // 异步resolve
  setTimeout(() => {
    resolve('ok1')
  }, 1000);
});

p.then((res) => {
  console.log('res1', res);
  // 返回thenable对象
  return {
    then: function (resolve) {
      setTimeout(() => {
        resolve('ok2')
      }, 1000);
    }
  }
}).then((res) => {
  console.log('res2', res);
  // 返回Promise对象
  return new MyPromise((resolve) => {
    // resolve一个Promise对象
    resolve(new MyPromise((resolve) => {
      setTimeout(() => {
        resolve('ok3')
      }, 1000)
    }))
  });
}).then((res) => {
  console.log('res3', res);
})

resolve reject all race 实现

原生Promise还有一些静态方法

class MyPromise {
  static resolve(param) {
    // Promise对象,直接返回
    if (param instanceof MyPromise) {
      return param;
    }
    return new MyPromise((resolve, reject) => {
      // thenalbe对象
      if (isThenable(param)) {
        setTimeout(() => {
          param.then(resolve, reject);
        });
      } else {
        // 普通对象
        resolve(param);
      }
    });
  }

  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }

  static race(promises) {
    return new MyPromise((resolve, reject) => {
      if (promises.length === 0) {
        return;
      } else {
        for (let i = 0; i < promises.length; i++) {
          // 先MyPromise.resolve包装一下,防止传入数组里面的对象是普通对象而不是Promise对象
          MyPromise.resolve(promises[i]).then((data) => {
            resolve(data);
            return;
          }, (err) => {
            reject(err);
            return;
          });
        }
      }
    });
  }

  static all(promises) {
    return new MyPromise((resolve, reject) => {
      let index = 0;
      let result = [];
      if (promises.length === 0) {
        resolve(result);
      } else {
        function processValue(i, data) {
          result[i] = data;
          if (++index === promises.length) {
            resolve(result);
          }
        }
        for (let i = 0; i < promises.length; i++) {
          MyPromise.resolve(promises[i]).then((data) => {
            processValue(i, data);
          }, (err) => {
            reject(err);
            return;
          });
        }
      }
    });
  }
  
}

catch finally 实现

原生Promise对象还有catchfinally两个原型方法,我们也可以实现

class MyPromise {
  catch(onRejected) {
    // 返回一个新的Promise对象,同时then方法只注册一个失败的回调函数
    return this.then(null, onRejected);
  }

  finally(callback) {
    // finally返回的Promise对象,then方法拿到的res值不是callback返回的,而是上一个Promise的
    return this.then((value) => {
      // MyPromise.resolve包装callback的返回值
      return MyPromise.resolve(callback()).then(() => {
        return value;
      });
    }, (err) => {
      return MyPromise.resolve(callback()).then(() => {
        throw err;
      });
    });
  }

}

测试一下

MyPromise.reject('test1').catch((err) => {
  console.log('err', err);
}).finally(() => {
  console.log('fianlly');
})

MyPromise.resolve('test2').then((res) => {
  console.log('res', res);
})

MyPromise.race([
  new MyPromise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),
  new MyPromise((resolve, reject) => { setTimeout(() => { resolve(223232) }, 100) })
]).then((data) => {
  console.log('success ', data);
}, (err) => {
  console.log('err ',err);
});

MyPromise.all([
  new MyPromise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),
  new MyPromise((resolve, reject) => { setTimeout(() => { resolve(223232) }, 100) })
]).then((data) => {
  console.log('success ', data);
}, (err) => {
  console.log('err ',err);
});

完整Promise

点击展开源代码 ```javascript const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected';

function isThenable(obj) { return !!obj && (typeof obj === ‘object’ || typeof obj === ‘function’) && typeof obj.then === ‘function’; }

class MyPromise { constructor(executor) { this.status = PENDING; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; this.value = ”; this.reason = ”;

try {
  executor(this._resolve.bind(this), this._reject.bind(this));
} catch (e) {
  this._reject(e);
}

}

_resolve(value) { // Promise对象或者thenable对象 if (isThenable(value)) { return value.then(this._resolve.bind(this), this._reject.bind(this)) } if (this.status === PENDING) { this.status = FULFILLED; this.value = value; this.onFulfilledCallbacks.forEach(fn => fn()); } }

_reject(reason) { if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach(fn => fn()); } }

then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === ‘function’ ? onFulfilled : value => value; onRejected = typeof onRejected === ‘function’ ? onRejected : reason => { throw reason }; const promise2 = new MyPromise((resolve, reject) => { if (this.status === FULFILLED) { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject);

      } catch (e) {
        reject(e);
      }
    })
  }

  if (this.status === REJECTED) {
    setTimeout(() => {
      try {
        let x = onRejected(this.reason);
        resolvePromise(promise2, x, resolve, reject);
      } catch (e) {
        reject(e);
      }
    })
  }

  if (this.status === PENDING) {
    this.onFulfilledCallbacks.push(() => {
      setTimeout(() => {
        try {
          let x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      })
    })

    this.onRejectedCallbacks.push(() => {
      setTimeout(() => {
        try {
          let x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      })
    })
  }
});

return promise2;

}

catch(onRejected) { return this.then(null, onRejected); }

finally(callback) { return this.then((value) => { return MyPromise.resolve(callback()).then(() => { return value; }); }, (err) => { return MyPromise.resolve(callback()).then(() => { throw err; }); }); }

static resolve(param) { if (param instanceof MyPromise) { return param; } return new MyPromise((resolve, reject) => { if (isThenable(param)) { setTimeout(() => { param.then(resolve, reject); }); } else { resolve(param); } }); }

static reject(reason) { return new MyPromise((resolve, reject) => { reject(reason); }); }

static race(promises) { return new MyPromise((resolve, reject) => { if (promises.length === 0) { return; } else { for (let i = 0; i < promises.length; i++) { // 先MyPromise.resolve包装一下,防止传入数组里面的对象是普通对象而不是Promise对象 MyPromise.resolve(promises[i]).then((data) => { resolve(data); return; }, (err) => { reject(err); return; }); } } }); }

static all(promises) { return new MyPromise((resolve, reject) => { let index = 0; let result = []; if (promises.length === 0) { resolve(result); } else { function processValue(i, data) { result[i] = data; if (++index === promises.length) { resolve(result); } } for (let i = 0; i < promises.length; i++) { MyPromise.resolve(promises[i]).then((data) => { processValue(i, data); }, (err) => { reject(err); return; }); } } }); }

}

function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { reject(new TypeError(‘循环引用’)); }

// Promise对象或者thenable对象 if (isThenable(x)) { try { x.then(resolve, reject) } catch (e) { reject(e); } } else { // 其他普通对象 resolve(x); } }

</details>

### 参考
- [深究Promise的原理及其实现](https://github.com/yonglijia/JSPI/blob/master/How%20to%20implement%20a%20Promise.md)
- [Promise的源码实现](https://github.com/YvetteLau/Blog/issues/2)
- [一步一步实现一个Promise](https://github.com/xieranmaya/blog/issues/3)
- [简单实现Promise](https://imweb.io/topic/5bbc264b6477d81e668cc930)

Share this post on:

Previous Post
web支付基础教程
Next Post
jsbridge初探