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实现,但是大大提升了代码的可读性。