Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从零开始实现promise(一) #37

Open
keep-run opened this issue Jun 30, 2021 · 0 comments
Open

从零开始实现promise(一) #37

keep-run opened this issue Jun 30, 2021 · 0 comments

Comments

@keep-run
Copy link
Owner

keep-run commented Jun 30, 2021

概要

promise的使用现在已经非常普及了。 虽然方便了开发者,但是也带了一些八股文考题,常见的就是如何实现一个promise. 下边从零开始,一步步实现一个完整的promise

简易版分析

简易版的promise应该具有以下基础功能:

  • 新建promise;
  • promise状态正常流转;
  • then方法;

简单实现

状态管理

首先,一个promise具有三个状态pendingfulfilledrejected。且只能按照如下方式流转

  • pending ---> fulfilled
  • pending ---> rejected

状态一旦改变,就不再变了。

其次,创建Promise的时候,接受一个函数,且会立即执行,该函数接受两个函数参数,resolve,reject,用来改变状态;

基于此可以实现如下代码

// 先定义三个状态变量
const PENDING = 'pending'
const REJECTED = 'rejected'
const FULFILLED = 'fulfilled'

class MyPromise {
  state = PENDING
  value = ''   // 向后传的value值

  constructor(fn) {
    if (typeof fn !== "function") {
      throw new Error('参数必须是函数')
    }
    fn(this._resolve.bind(this), this._reject.bind(this))
  }


  _resolve(value) {
    this.state = FULFILLED  //修改状态
    this.value = value      // 赋值,用于向后传递
  }

  _reject(error) {
    this.state = REJECTED  //修改状态
    this.value = error      // 赋值,用于向后传递
  }
}

then

thenpromise的一个基本且强大方法,具有以下特点

  • 接受两个函数参数onFulfilled, onRejected;分别承接 resolvereject的回调;
  • then方法会返回一个新的promise,因此可以实现链式调用,一直then下去;

实现then的一个难点就是,每次then方法会注册两个函数参数onFulfilled, onRejected,但是具体执行哪一个,依赖前一个promise的状态,fulfilled状态时调用onFulfilledrejected状态时调用onRejected。 因此onFulfilled, onRejected必须注册在前一个promise的回调队列中,以便状态变更时,正确回调;

基于以上分析,完善代码如下

// 先定义三个状态变量
const PENDING = 'pending'
const REJECTED = 'rejected'
const FULFILLED = 'fulfilled'

class MyPromise {

  state = PENDING
  value = ''      // 向后传的value值
  callbacks = []  // 回调队列

  constructor(fn) {
    if (typeof fn !== "function") {
      throw new Error('参数必须是函数')
    }
    fn(this._resolve.bind(this), this._reject.bind(this))
  }

  then(onFulfilled, onRejected) {
    const _this = this;   // _this指向前一个promise对象,这个很关键
    return new MyPromise((resolve, reject) => {
      _this._handle({
        onFulfilled,
        onRejected,
        resolve,
        reject
      })
    })
  }

  _resolve(value) {
    if (this.state !== PENDING) {
      return
    }
    this.state = FULFILLED  // 修改状态
    this.value = value      // 赋值,用于向后传递
    this.callbacks.forEach(cb => this._handle(cb))  //回调then中的注册函数
  }

  _reject(error) {
    if (this.state !== PENDING) {
      return
    }
    this.state = REJECTED   // 修改状态
    this.value = error      // 赋值,用于向后传递
    this.callbacks.forEach(cb => this._handle(cb))  //回调then中的注册函数
  }
  _handle(callback) {
    // pending状态时,注册函数入栈
    if (this.state === PENDING) {
      this.callbacks.push(callback)
      return
    }
    // 按需获取then中注册的回调函数
    const cb = this.state === FULFILLED ? callback.onFulfilled : callback.onRejected

    // 改变then返回的promise的状态,持续回调后续的then
    const cb_changeState = this.state === FULFILLED ? callback.resolve : callback.reject

    let ret

    if (cb) {
      ret = cb(this.value)  // 计算继续向后传递的值
    }
    cb_changeState(ret)
  }
}

demo

new MyPromise((resolve, reject) => {
  resolve('test')
}).then((value) => {
  console.log('then1', value)
}, (err) => {
  console.log('reject1', err)
}).then((value) => {
  console.log('then2', value)
}, (err) => {
  console.log('reject2', err)
})

最终输出如下:

then1,test
then2,undefined

遗留问题

上述代码虽然实现了状态变化以及链式调用等基本能力,但是会存在一个很大的问题,第一个promise的状态变为fulfilled,那么会依次调用后续的onFulfilled函数。这是不符合预期的。

预期是当前then的回调函数,只取决于前一个promise的状态,即整个回调链路中可能同时存在onFulfilledonRejected

在下一篇文章中将专门解决这个问题。

参考文献

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant