Promise 简单实现

今天的任务是根据Promise A+ 规范简单实现一个 Promise 库。开始之前,我们可以先通读一下这个规范,戳这里

静心读一下这个规范真的很重要,很重要,很重要。虽然是英文版,但是读起来也许会比汉语更能让人理解。

A promise represents the eventual result of an asynchronous operation. The primary way of interacting with a promise is through its then method, which registers callbacks to receive either a promise’s eventual value or the reason why the promise cannot be fulfilled.

提到 Promise 自然想到异步,异步想到回调,回调就是回头在调。回头在调的前提是提前存储回调内容,等到时间后从存储的地方取到对应的内容再执行即可。

一、Promise 构造函数实现

2.1 A promise must be in one of three states: pending, fulfilled, or rejected.

2.1.1 A pending promise may transition to either the fulfilled or rejected state.

2.1.2 A fulfilled promise must have a value, which must not change.

2.1.3 A rejected promise must have a reason, which must not change.

我们平时但凡做什么事情,大概都有三种基本状态,正在进行、成功、失败。一个 Promise 代表一个异步操作,也有这三种状态,且状态只能从正在进行到成功或者失败,不可逆转。不管事情最终什么状态,既然结果出来了,那就通知之前缓存回调的数组(因为可以 then 多次,故为数组)并把结果给它吧。

Promise 基本使用:

let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('success')
    }, 1000)
})
promise.then((data) => {console.log(data)})
复制代码

首先 Promise 是一个类,通过 new 创建一个实例,参数是一个函数,函数参数默认命名为 resolvereject,我们称该函数为一个执行器 executor,默认执行。构造函中还需要一个状态变量 status,保存当前执行状态;以及执行成功 value 或失败 reason 的结果变量;最后还需要上面提及的缓存回调的数组。实现代码如下:

class Promise () {
    constructor (executor) {
        // 默认等待态
        this.status = 'pending'
        // 成功结果
        this.value = undefined
        // 失败原因
        this.reason = undefined
        // 存储成功回调数组
        this.onResolvedCallbacks = []
        // 存储失败回调数组
        this.onRejectedCallbacks = []
        // 执行器
        executor(resolve, reject)
    }
}
复制代码

执行器函数参数是 resolvereject, 同样也是函数,功能是有结果后触发缓存回调数组,也可以理解为发布。实现代码如下:

class Promise () {
    constructor (executor) {
        // 变量声明
        this.status = 'pending'
        this.value = undefined
        this.reason = undefined
        this.onResolvedCallbacks = []
        this.onRejectedCallbacks = []
        // 结果成功,通知函数定义
        let resolve = (value) => {
            if (this.status === 'pending') {
                this.status = 'resolved'
                this.value = value
                this.onFulfilledCallbacks.forEach(fn => fn())
            }
        }
        // 结果失败,通知函数定义
        let reject = (reason) => {
            if (this.status === 'pending') {
                this.status = 'rejected'
                this.reason = reason
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }
    }
}
复制代码

既然有结果后要触发缓存回调数组,那么接下来我们看一下这个回调数组如何缓存。

二、Promise.then 实现

2.2 A promise must provide a then method to access its current or eventual value or reason.

1、then 订阅功能

我们都知道,promise 实例只有通过调用 then 函数才能拿到结果,then 函数参数分别为成功回调和失败回调,回调参数为成功值和失败值。固 then 函数的主要功能即是缓存回调函数,也可以理解为订阅。实现代码如下:

class Promise () {
    // constructor省略...
    then (onFulfilled, onRejected) {
        // 如果exector内容为同步代码,resolve函数执行后,状态已变为resolved,则直接执行回调
        if (this.status === 'resolved') {
            onFulfilled(this.value)
        } 
        // 如果exector内容为同步代码,reject函数执行后,状态已变为rejected,直接执行回调
        if (this.status === 'rejected') {
           onRejected(this.reason)
        }
        // 如果exector内容为异步代码,状态为pending时,则缓存回调函数,我们也可以理解为订阅
        if (this.status === 'pending') {
            this.onResolvedCallbacks.push(() => {
               onFulfilled(this.value)
            })
            this.onRejectedCallbacks.push(() => {
                onRejected(this.reason)
            })
        }
    }
}
复制代码

2、then 返回 promise

2.2.6 Then may be called multiple times on the same promise.

2.2.7 Then must return a promise. promise2 = promise1.then(onFulfilled, onRejected)

实现代码如下:

class Promise () {
    // constructor省略...
    then (onFulfilled, onRejected) {
        let promise2
        if (this.status === 'resolved') {
            promise2 = new Promise((resolve, reject) => {
                onFulfilled(this.value)
            })
        }
        if (this.status === 'rejected') {
            promise2 = new Promise((resolve, reject) => {
                onRejected(this.reason)
            })
        }
        if (this.status === 'pending') {
            promise2 = new Promise((resolve, reject) => {
                this.onResolvedCallbacks.push(() => {
                    onFulfilled(this.value)
                })
                this.onRejectedCallbacks.push(() => {
                    onRejected(this.reason)
                })
            })
        }
        return promise2
    }
}
复制代码

2、then 调用多次

2.2.7.1 onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).

then 可以调用多次,且每次 then 参数 onFulFilled 函数的参数值为上一次 promise 函数的执行结果,即为 onFulfilled(this.value) 的执行结果,我们设为 x。根据 x 的不同情况做不同处理,详情见 Promise A+ 规范 2.3 Promise Resolution Procedure

实现代码如下:

class Promise () {
    // constructor省略...
    then (onFulfilled, onRejected) {
        let promise2
        if (this.status === 'resolved') {
            promise2 = new Promise((resolve, reject) => {
                let x = onFulfilled(this.value)
                // Promise Resolution Procedure
                resolvePromise(promise2, x, resolve. reject)
            })
        }
        if (this.status === 'rejected') {
            promise2 = new Promise((resolve, reject) => {
                let x = onRejected(this.reason)
                // Promise Resolution Procedure
                resolvePromise(promise2, x, resolve. reject)
            })
        }
        if (this.status === 'pending') {
            promise2 = new Promise((resolve, reject) => {
                this.onResolvedCallbacks.push(() => {
                    let x = onFulfilled(this.value)
                    // Promise Resolution Procedure
                    resolvePromise(promise2, x, resolve. reject)
                })
                this.onRejectedCallbacks.push(() => {
                    let x = onRejected(this.reason)
                    resolvePromise(promise2, x, resolve, reject)
                })
            })
        }
        return promise2
    }
}
// 处理函数:处理promise2和x的关系
function resolvePromise (promise2, x, resolve, reject) {
    // 2.3.1 If promise and x refer to the same object, reject promise with a TypeError as the reason.
    if (promise2 === x) {
        return reject(new TypeError('循环引用'))
    }
    //  2.3.3 if x is an object or function
    if (x !== null && typeof x === 'object' || typeof x === 'function' ){
        try {
            // 2.3.3.1 Let then be x.then.
            let then = x.then
            // 2.3.3.3 If then is a function,call it with x as this.
            if (typeof then === 'function') {
                // 2.3.3.3.1 If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
                then.call(x, y => {
                    resolvePromise(promise2, y, resolve, reject)
                }, r => {
                    // 2.3.3.3.2 If/when rejectPromise is called with a reason r, reject promise with r.
                    reject(r)
                })
            }
        } catch (e) {
            // 2.3.3.4 If calling then throws an exception e
            reject(e)
        }
    } else {
        // 2.3.4 If x is not an object or function, fulfill promise with x.
        resolve(x)
    }
}
复制代码

3、状态不可逆转

实现代码如下:

// 处理函数:处理promise2和x的关系
function resolvePromise (promise2, x, resolve, reject) {
    // 2.3.1 If promise and x refer to the same object, reject promise with a TypeError as the reason.
    if (promise2 === x) {
        return reject(new TypeError('循环引用'))
    }
    let called
    //  2.3.3 if x is an object or function
    if (x !== null && typeof x === 'object' || typeof x === 'function' ){
        try {
            // 2.3.3.1 Let then be x.then.
            let then = x.then
            // 2.3.3.3 If then is a function,call it with x as this.
            if (typeof then === 'function') {
                // 2.3.3.3.1 If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
                then.call(x, y => {
                    // `2.3.3.3.3 If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.`
                    if (called) return
                    called = true
                    resolvePromise(promise2, y, resolve, reject)
                }, r => {
                    if (called) return
                    called = true
                    // 2.3.3.3.2 If/when rejectPromise is called with a reason r, reject promise with r.
                    reject(r)
                })
            }
        } catch (e) {
            // `2.3.3.3.4 If calling then throws an exception e,2.3.3.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.`
            if (called) return
            called = true
            // 2.3.3.4 If calling then throws an exception e
            reject(e)
        }
    } else {
        // 2.3.4 If x is not an object or function, fulfill promise with x.
        resolve(x)
    }
}
复制代码

4、Promise.then 异步执行

实现代码如下:

class Promise () {
    then (onFulfilled, onRejected) {
        let promise2
        if (this.status === 'resolved') {
            promise2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            })
        }
        if (this.status === 'rejected') {
            promise2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }. 0)
            })
        }
        if (this.status === 'pending') {
            promise2 = new Promise((resolve, reject) => {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value)
                            promiseResolve(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason)
                            promiseResolve(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
            })
        }
        return promise2
    }
}
复制代码

5、Promise.then 穿透

promise.then().then() 多次,都可以获取到最终结果,固当 then 参数 onFulfilled 为空时,需要自动 return value;当 onRejected 为空时,需要 return throw err。实现代码如下:

class Promise () {
    then (onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => { return value }
        onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err }
        // promise2省略...
    }
}
复制代码

三、其它方法实现

1、Promise.resolve

实现代码如下:

Promise.resolve = function (val) {
    return new Promise((resolve, reject) => {
        resolve(val)
    })
}
复制代码

2、Promise.reject

实现代码如下:

Promise.reject = function (val) {
    return new Promise((resolve, reject) => {
        reject(val)
    })
}
复制代码

3、Promise.all

实现代码如下:

Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let dataArr = []
        let count = 0
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(data => {
                dataArr[i] = data
                count++
                if (count === promises.lenth) {
                    resolve(dataArr)
                }
            }, reject)
        }
    })
}
复制代码

4、Promise.race

实现代码如下:

Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            promise[i].then(resolve, reject)
        }
    })
}
复制代码

5、Promise.promisify

实现代码如下:

Promise.promisify = function (fn) {
    return function (...args) {
        return new Promise((resolve, reject) => {
            fn(...args, (err, data) => {
                if (err) reject(err)
                resolve(data)
            })
        })
    }
}
复制代码

四、Promise 校验

要想验证自己实现的 Promise 是否符合 Promise A+ 规范,可以全局安装 promises-aplus-tests 工具包。

Adapters

In order to test your promise library, you must expose a very minimal adapter interface. These are written as Node.js modules with a few well-known exports:

  • resolved(value): creates a promise that is resolved with value.
  • rejected(reason): creates a promise that is already rejected with reason.
  • deferred(): creates an object consisting of { promise, resolve, reject }

The resolved and rejected exports are actually optional, and will be automatically created by the test runner using deferred

resolvedrejected 是可选的,下面我们实现一下 deferred,实现代码如下:

Promise.deferred = function () {
    let dfd = {}
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd
}
复制代码

到这里一切准备就绪,校验步骤如下:

npm install promises-aplus-tests -g
promises-aplus-tests ./Promise.js
复制代码

源码

好了,到这里全部代码都已呈现,赶快自己亲手试一下吧!???

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值