手写Promise 面向结果编程

82 阅读4分钟

要想实现自己的promise,首先我们要知道promise的用法,执行代码后出现的效果是什么。

我们先从最基本的用法开始:

const promise = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('Promise返回值')
    },2000)
})
promise.then(res=>{
    console.log('后输出',res)
})
console.log('先输出111')

我们根据这段代码,来梳理下思路:

  1. 先声明一个class类,我们叫做 MyPromise ;
  2. MyPromise 类 构造函数接收一个回调函数 做为参数,且该回调函数有两个参数 分别为 resolve(成功) reject(失败),均为函数;
  3. MyPromise还有一个实例可调用的then 方法,该方法同样接收两个参数 分别为 成功的回调函数 和 失败的回调函数

接下来我们实现这个函数

class MyPromise {
    constructor(fn) {
        this.promiseState = 'pendding'; //设置初始状态
        this.resolveFn = null; //保存then 方法中成功的回调
        this.rejectFn = null; // 保存then 方法中失败的回调
        fn(this.resolve, this.reject)
    }
    resolve(result) {
        if(this.promiseState==='pendding'){ 
            this.promiseState = 'fulfilled'
            if (this.resolveFn) {
                this.resolveFn(result)
            }
        }
    }
    reject(reason) {
        if(this.promiseState==='pendding'){
           this.promiseState = 'rejected'
           if (this.rejectFn) {
             this.rejectFn(reason)
           }
        }
    }
    then(resolve, reject) {
        resolve && (this.resolveFn = resolve);
        reject && (this.rejectFn = reject)
    }
}

下面我们来测试一下上面写的MyPromise类:

//测试用例1 异步
const promise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('Promise返回值')
    }, 2000)
})
promise.then(res=>{
    console.log('后输出',res)
})
console.log('先输出111')

运行上面的代码 发现 控制台报错,并不是我们想要的输出结果

Uncaught TypeError: Cannot read properties of undefined (reading 'PromiseState') 
    at resolve (<anonymous>:10:17)
    at <anonymous>:36:9

从报错信息我们可以看出 在 执行resolve这个回调方法的时候 undefined 不能读取他的 PromiseState 属性

那么看一下我们定义的resolve方法里是 this.PromiseState 那么就是thisundefined 那么只能是this指向问题

解决classthis指向 一般会用箭头函数或者bind,在这里我们就可以使用bind来绑定this,那么只需要将MyPromise类的构造函数constructor 中的this.resolvethis.reject后加上.bind(this)就可以了。

我们改造后的构造方法就是下面这样子的

 constructor(fn) {
      this.promiseState = 'pendding'; //设置初始状态
      this.resolveResult = null;
      this.rejectReason = null;
      fn(this.resolve.bind(this), this.reject.bind(this))
 }

在执行上面的测试用例 发现可以正常运行 且控制台输出结果和 new Promise 时是一样的

我们知道 Promise 构造函数就是需要一个 函数, 这个函数可以是一个异步操作 同理也可以是一个同步执行的函数 这时候我们在测一下我们的 MyPromise

//测试用例2 同步
const promise = new MyPromise((resolve, reject) => {
    resolve('Promise返回值')
})
promise.then(res => {
    console.log('后输出', res)
})
console.log('先输出111')

这时候我们发现 只有then 方法后面的输出 并没有 then 方法里面内容的输出 且没有报错信息,说明我们调用then 方法传入的函数没有执行

重新梳理一下思路:

  1. 创建实例的时候 回调函数里会调用 MyPromise 这个类的 resove 方法 这个方法里 我们改变了 实例的状态 从 pendding 状态 改为 fulfilled 状态 且 会立即改变 然后执行了 调用 then 方法时 赋值给 resolveFn 的方法 但是这时候还没有执行到调用 then 方法
  2. 我们创建完实例之后 调用 then 方法 这时候 我们把成功的回调和失败的回调分别赋值给 resolveFnrejectFn 但是没有调用

那么问题就很清晰了 我们第一步执行 resolveFn 这个方法的时候 回调函数还没有放进去 那么我们的回调函数肯定不会被调用

解决方法随之 也出来了 我们在 then 方法里加判断 当不是 pendding 状态的时候直接调用回调函数

then(resolve, reject) {
    if(this.promiseState === 'pendding'){
       resolve && (this.resolveFn = resolve);
       reject && (this.rejectFn = reject)
    }else if(this.promiseState === 'fulfilled'){
        resolve()
    }else if(this.promiseState === 'rejected'){
        reject()
    }
}

执行上面的 测试用例2 发现 输出顺序不对 且 返回值是没有的 输出顺序不对说明then方法没有异步执行,没有返回值我们就在实例上面加一个 保存执行结果的属性 修改后的 MyPromise 也就是这样子的

class MyPromise {
    constructor(fn) {
        this.promiseState = 'pendding'; //设置初始状态
        this.resolveFn = null; //保存then 方法中成功的回调
        this.rejectFn = null; // 保存then 方法中失败的回调
        this.resolveResult = null;
        this.rejectReason = null;
        fn(this.resolve.bind(this), this.reject.bind(this))
    }
    resolve = (result) => {
        if (this.promiseState === 'pendding') {
            this.promiseState = 'fulfilled'
            this.resolveResult = result;
            if (this.resolveFn) {
                this.resolveFn(result)
            }
        }
    }
    reject(reason) {
        if (this.promiseState === 'pendding') {
            this.promiseState = 'rejected'
            this.rejectReason = reason;
            if (this.rejectFn) {
                this.rejectFn(reason)
            }
        }
    }

    then(resolve, reject) {
        setTimeout(() => {
            if (this.promiseState === 'pendding') {
                resolve && (this.resolveFn = resolve);
                reject && (this.rejectFn = reject)
            } else if (this.promiseState === 'fulfilled') {
                resolve(this.resolveResult)
            } else if (this.promiseState === 'rejected') {
                reject(this.rejectReason)
            }
        })
    }
}