ES6 Promise 及实现原理



Promise的诞生:

js属于单线程,异步的语言,大部分的异步操作都是依靠回调函数进行处理,如果遇到发送请求依赖上个请求的结果进行下次请求,就会出现嵌套的问题,如下:


var fs = require('fs')
// 每个请求的地址依赖上一个请求
fs.readFile('./name.txt', function (error, data) {
    let agePath = data
    if (data) {
        fs.readFile(agePath, function (error, data) {
            let sexPath = data
            if (data) {
                fs.readFile(sexPath, function (error, data) {
                    console.log(sexPath)
                })
            }
        })
    }
})复制代码

大量的嵌套导致代码的可读性变差,维护困难,由此诞生了promise

下面我们来使用一下Promise

let p = new Promise((resolve, reject) => {
    setTimeout(function () {
        resolve('我是p1')
    }, 2000)
})

console.log(promise);

p.then(data => {
    console.log(data)
}, err => {
    console.log(err)
})复制代码

首先是new Promise传入一个构造器,返回一个Promise对象,构造器内有一个resolve和reject方法,分别用来触发promise的成功失败,then内有两个参数,第一个是注册成功的回调,第二个是注册失败的回调函数


简单实现

function Promise(executor) {
    //为什么要用self
    //因为promise捕获异步的结果,resolve和reject用来触发成功失败的时候this指向已经改变
    let self = this
    self.status = 'pending'
    self.value = undefined
    self.reason = undefined
    // 成功回调数组
    self.onFulfilled = []
    // 失败回调数组
    self.onRejected = []
    // 一旦promise状态变为成功或者失败就不会在改变
    function resolve(value) {
        if (self.status !== 'pending') return
        self.status = 'resolved'
        self.value = value
        self.onFulfilled.forEach(fn => fn())
    }
    function reject(reason) {
        if (self.status !== 'pending') return
        self.status = 'rejected'
        self.reason = reason
        self.onRejected.forEach(fn => fn())
    }
    // 捕获错误,如果Promise实例化出现错误直接执行reject
    try {
        executor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}

// then函数用来注册成功失败的回调
Promise.prototype.then = function (onFulfilled, onRejected) {
    let self = this
    // 还在等待结果的时候需要注册到回调数组内
    if (self.status === 'pending') {
        self.onFulfilled.push(() => {
            onFulfilled(self.value)
        })
        self.onRejected.push(() => {
            onRejected(self.reason)
        })
    }
    // 如果调用.then的时候状态已经是resolved了
    // 我们直接调用方法,因为之前的数组可能执行过了
    // 情况出现在回调完成后,再次调用then的方法
    if (self.status === 'resolved') {
        onFulfilled(self.value)
    }
    // 同理上面
    if (self.status === 'rejected') {
        onRejected(self.reason)
    }
}复制代码


以上代码实现简单的Promise

Promise还支持链式调用,我们来看看链式调用的实现方法,需要修改then方法

function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        // 防止循环引用死循环
        // 例如
        // let p = new Promise((resolve, reject) => {
        //     resolve(123)
        // })
        // p.then(data => {
        //     return p
        // })
        return reject(new TypeError('循环引用'))
    }
    // 创建变量用来标记,如果进入成功就不会触发失败,防止引用其他人的Promise库同时触发成功和失败
    let called
    // 如果返回值是函数或者是对象,就有可能是其他人return的Promise对象,我们需要等他成功后将结果返回到下一个Promise中
    // 如果返回值是其他类型,直接抛出到下个then的参数中
    if (x !== null && (typeof x === 'function' || typeof x === 'object')) {
        // 捕获异常,如果then函数存在错误,直接抛出
        try {
            let then = x.then
            if (typeof then === 'function') {
                then.call(x, (y) => {
                    // 递归处理,防止返回值还是Promise,直到处理完类型不是函数或者对象抛出
                    resolvePromise(promise2, y, resolve, reject)
                }, (e) => {
                    if (called) return
                    called = true
                    reject(e)
                })
            } else {
                // 不是函数结果
                if (called) return
                called = true
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true
            reject(e)
        }
    } else {
        if (called) return
        called = true
        resolve(x)
    }
}

// then函数用来注册成功失败的回调
Promise.prototype.then = function (onFulfilled, onRejected) {
    let self = this
    // 创建一个promise2作为返回值
    let promise2
    promise2 = new Promise((resolve, reject) => {
        // 还在等待结果的时候需要注册到回调数组内
        if (self.status === 'pending') {
            self.onFulfilled.push(() => {
                let x = onFulfilled(self.value)
                resolvePromise(promise2, x, resolve, reject)
            })
            self.onRejected.push(() => {
                let x = onRejected(self.reason)
                resolvePromise(promise2, x, resolve, reject)
            })
        }
        // 如果调用.then的时候状态已经是resolved了
        // 我们直接调用方法,因为之前的数组可能执行过了
        // 情况出现在回调完成后,再次调用then的方法
        if (self.status === 'resolved') {
            let x = onFulfilled(self.value)
            resolvePromise(promise2, x, resolve, reject)
        }
        // 同理上面
        if (self.status === 'rejected') {
            let x = onRejected(self.reason)
            resolvePromise(promise2, x, resolve, reject)
        }
    })
    return promise2
}


new Promise((resolve, reject) => {
    resolve(123)
}).then(data => {
    console.log(data)  //输出123
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(456)
        }, 1000)
    })
}).then(data => {
    console.log(data)  //输出456
})复制代码


resolvePromise是一个处理函数,当上一个Promise成功后会调用下一个Promise的resolve, reject来触发下一个Promise的回调

现在我们来完善一下代码


// then函数用来注册成功失败的回调
Promise.prototype.then = function (onFulfilled, onRejected) {
    // 如果没有传参,默认返回上一个结果到下个Promise中,在下个Promise的then中调用
    if (typeof onFulfilled !== 'function') onFulfilled = data => data
    // 错误必须throw错误不然返回出来就是上一个Promise的成功了,throw会被try,catch捕获,执行reject
    if (typeof onRejected !== 'function') onRejected = err => {
        throw err
    }
    let self = this
    // 创建一个promise2作为返回值
    let promise2
    promise2 = new Promise((resolve, reject) => {
        // 还在等待结果的时候需要注册到回调数组内
        if (self.status === 'pending') {
            self.onFulfilled.push(() => {
                try {
                    let x = onFulfilled(self.value)
                    // 处理函数,当上一个Promise成功后会调用下一个Promise的resolve, reject来触发下一个Promise的回调
                    resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            })
            self.onRejected.push(() => {
                try {
                    let x = onRejected(self.reason)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            })
        }
        // 如果调用.then的时候状态已经是resolved了
        // 我们直接调用方法,因为之前的数组可能执行过了
        // 情况出现在回调完成后,再次调用then的方法
        if (self.status === 'resolved') {
            try {
                let x = onFulfilled(self.value)
                resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
                reject(e)
            }
        }
        // 同理上面
        if (self.status === 'rejected') {
            try {
                let x = onRejected(self.reason)
                resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
                reject(e)
            }
        }
    })
    return promise2
}

// 直接执行一个只有失败回调的函数
// 如果前面没有写失败回调,会throw到后面有失败回调或者是catch内
Promise.prototype.catch = function (onRejected) {
    this.then(null, onRejected)
}复制代码


之前说的处理都是按顺序处理异步,如果同时执行多个异步函数,Promise有一个Promise.all方法,专门用来处理异步,传入一个Promise数组,返回一组Promise的结果,如果失败一个就进入失败回调


let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    }, 500)
})

let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2)
    }, 500)
})
Promise.all([p1, p2]).then(list => {
    console.log(list)
}, err => {
    console.log(err)
})复制代码


我们接下来看看实现Promise.all的实现


Promise.all = function (promises) {
    let result = []
    let index = 0
    return new Promise((resolve, reject) => {
        function processData(i, data) {
            result[i] = data
            index++
            if (index === promises.length) {
                resolve(result)
            }
        }
        promises.forEach((promise, i) => {
            promise.then(data => {
                processData(i, data)
            }, reject)
        })
    })
}复制代码

按顺序执行,resolve后把结果赋值给result对应的索引,然后当index和Promise长度一样讲数组丢出到返回Promise中,返回顺序和输入保持一致


Promise的实现原理大致就是这样了,虽然写法是这样的,但是本质还是通过callback实现,但是大大提升了代码的可读性。


转载于:https://juejin.im/post/5b86564bf265da434f218d4d

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值