什么是 Promise ?

311 阅读4分钟

1) Promise 基本特性

  • Promise 有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。
  • Promise 对象接受一个回调函数作为参数,该回调函数接受两个参数,分别是成功时的回调 resolve 和失败时的回调 reject;另外 resolve 的参数除了正常值以外,还可能是一个 Promise 对象的实例;reject 的参数通常是一个 Error 对象的实例。
  • then 方法返回一个新的 Promise 实例,并接收两个参数 onResolved(fulfilled 状态的回调)、onRejected(rejected 状态的回调,该参数可选)。
  • catch 方法返回一个新的 Promise 实例。
  • finally 方法不管 Promise 状态如何都会执行,该方法的回调函数不接受任何参数。
  • Promise.all() 方法将多个 Promise 实例包装成一个新的 Promise 实例,该方法接收一个由 Promise 对象组成的数组作为参数(Promise.all() 方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例),注意参数中只要有一个实例触发 catch 方法,都会触发 Promise.all() 方法返回新的 Promise 实例的 catch 方法,如果参数中的某个实例本身调用了 catch 方法,将不会触发 Promise.all() 方法返回的新的实例的 catch 方法。
  • Promise.race() 方法的参数与 Promise.all() 方法一样,参数中的实例只要有一个率先改变状态就会将该实例的状态传给 Promise.race() 方法,并将该返回值作为 Promise.race() 方法产生的 Promise 实例的返回值。
  • Promise.resolve() 将现有对象转为 Promise 对象,如果该方法的参数为一个 Promise 对象,Promise.resolve() 将不做任何处理;如果参数是一个 thenable 对象(即具有 then 方法),promise.resolve() 将该对象转为 Promise 对象并立即执行 then 方法;如果参数是一个原始值,或者是一个不具有 then 方法的对象,则 Promise.resolve() 方法返回一个新的 Promise 对象,状态为 fulfilled,其参数将会作为 then 方法中 onResolved 回调函数的参数;如果 Promise.resolve 方法不带参数,会直接返回一个 fulfilled 状态的 Promise 对象。需要注意的是,不带参数的情况下,返回的 Promise 对象是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。
  • Promise.reject() 同样返回一个新的 Promise 对象,状态为 rejected,无论传入任何参数都将作为 reject() 的参数。

2)Promise 优点

  1. 统一异步 API
    • Promise 的一个重要优点是它将逐渐被用作浏览器的异步 API,统一现在各种各样的 API,以及不兼容的模式和手法。
  2. Promise 与事件对比
    • 和事件相比较,Promise 更适合处理一次性的结果,在结果计算出来之前或之后注册回调函数都是可以的,都可以拿到正确的值。Promise 的这个优点很自然,但是不能使用 Promise 处理多次触发的事件。链式处理是 Promise 的又一优点,但是事件却不能这样链式处理。
  3. Promise 与回调对比
    • 解决了回调地狱的问题,将异步操作以同步操作的流程表达出来。
  4. Promise 带来的额外好处是包含了更好的错误处理方式(包含了异常处理),并且写起来很轻松(因为可以重用一些同步的工具,比如 Array.prototype.map())。

3)Promise 缺点

  • 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise 内部抛出的错误不回反应到外部。
  • 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
  • Promise 真正执行回调的时候,定义 Promise 那部分实际上已经走完了,所以 Promise 的报错堆栈上下文不太友好。

4)简单代码实现

最简单的 Promise 实现有7个主要属性,state(状态)、value(成功返回值)、reason(错误信息)、resolve 方法、reject 方法、then 方法。

class Promise {
    constructor(executor) {
        this.state = 'pending'
        this.value = undefined
        this.reason = undefined
        let resolve = value => {
            if(this.state === 'pending') {
                this.state = 'fulfilled'
                this.value = value
            }
        }
        let reject = reason => {
            if(this.state === 'pending') {
                this.state = 'rejected'
                this.reason = reason
            }
        }
        try {
            // 立即执行函数
            executor(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
    then(onFulfilled, onRejected) {
        if(this.state === 'fulfilled') {
            onFulfilled(this.value)
        } else if(this.state === 'rejected') {
            onRejected(this.reason)
        }
        
    }
}

5)面试够用版

function myPromise(constructor) {
    let self = this
    self.status = 'pending' // 定义状态改变前的初始状态
    self.value = undefined // 定义状态为 resolved 的时候的状态
    self.reason = undefined // 定义状态为 rejected 的时候的状态
    
    function resolve(value) {
        // 两个 === 'pending',保证了状态的改变是不可逆的
        if(self.status === 'pending') {
            self.value = value
            self.status = 'resolved'
        }
    }
    function reject(reason) {
        // 两个 === 'pending',保证了状态的改变是不可逆的
        if(self.status === 'pending') {
            self.value = reason
            self.status = 'rejected'
        }
    }
    // 捕获构造异常
    try {
        constructor(resolve, reject)
    } catch(e) {
        reject(e)
    }
}
myPromise.prototype.then = function(onFulfilled, onRejected) {
    let self = this
    switch(self.status) {
        case 'resolved':
            onFulfilled(self.value)
            break
        case 'rejected':
            onRejected(self.reason)
            break
    }
}
// 测试
var p = new myPromise(function(resolve) { resolve(1) })
p.then(function(res) { console.log(res) })
// 输出1