简单介绍:Promise函数是ES6一个重要的部分
new promise对象
promise是一个构造函数,自身有reject
、resolve
、race
方法,原型链上有then
、catch
方法。用new出来一个promise对象会自带这些方法。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
let number = parseInt(Math.random() * 100)
if (number > 50) {
resolve(number)
} else {
reject(number)
}
console.log('数据11111111111111111111111')
}, 3000)
})
// 3秒后 打印了 数据11111111111111111111111
在这里只是new了一个对象,并没有调用它,传进去的函数就已经执行了,这是需要注意的一个细节。所以用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数
then
方法
then
可以接收两个参数,是函数(即resolve
和reject
方法),并且会拿到刚才在p1中调用resolve
、reject
时传的的参数。----在这里resolve
、reject
相当于回调函数。
p1.then(resolve => {
console.log('随机数大于50,resolve结果:', resolve)
}, reject => {
console.log('随机数小于50,reject结果:', reject)
})
catch
方法
其实它和then
的第二个参数一样,用来指定reject
的回调。另外一个作用:在执行resolve
时(then
中的第一个参数),如果抛出异常了(代码语法错误或者单词拼写错误),那么并不会报错卡死js
,而是会进到这个catch
方法中。
p1.then(resolve => {
console.log('随机数大于50,resolve结果:', resolve)
})
.catch(error => {
console.log('随机数小于50,error结果:', error)
})
手动实现:一个手写模拟Promise
函数机制
因为JS
无法操作底层的微任务,所以这里用setTimeout
来模拟
首先要明白Promise
函数的两个特点:
-
Promise
对象的状态不受外界影响-
pending
(进行中) -
fulfilled
(已成功) -
rejected
(已失败)
-
-
一旦状态改变,就不会再变,任何时候都可以得到这个结果
状态改变,只有两种情况- 从
pending
变为fulfilled
- 从
pending
变为rejected
- 从
只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时候就成为resolved
(已定型)。
PS:提一下这个方面Promise
与实践Event
的区别:
如果Promise
对象状态改变已经发生了,再对Promise
添加回调函数,也会立即得到状态已经改变的结果。而事件Event
则不同,它是:如果错过了事件,再去监听,是得不到结果的。
先实现一个简易版的
function MyPromise (executor) {
// 保存当前的 Promise 实例
let self = this
// 初始化当前的 Promise 实例
self.value = null
self.error = null
self.status = PENDING
// 成功的回调函数初始化
// 为什么要写成数组,
// 因为有时候要绑定多个回调函数,
// 想要在 resolve() 之后一起执行
// 需要将 onFulfilled 和 onRejected 改为数组,
// 调用 resolve 时将其中的方法拿出来一一执行即可。
self.onFulfilledCallbacks = []
// 失败的回调函数初始化
self.onRejectedCallbacks = []
// 处理成功以后的回调函数
const resolve = (value) => {
// 判断 Promise 实例的状态,执行 resolve
// 或者reject 函数之前,状态应该是pending·
// Promise 实例的状态一旦改变 则凝固,不能再变
if (self.status !== PENDING) return
setTimeout(() => {
// 改变状态 为 fulfilled
self.status = FULFILLED
self.value = value // 这个是要传递出去的值
// resolve 成功时执行这个回调
// console.log('实例里面成功的函数')
// 这里的 onFulfilled 是经过 then 方法里面赋值的
self.onFulfilledCallbacks.forEach(
(callback) => callback(self.value)
)
})
}
// 处理失败后的回调函数
const reject = (error) => {
if (self.status !== PENDING) return
setTimeout(() => {
self.status = REJECTED
self.error = error
// console.log('实例里面失败的函数')
// reject 失败后执行这个回调
// 这里的 onRejected 是经过 then 方法里面赋值的
// self.onRejected(self.error)
self.onRejectedCallbacks.forEach(
(callback) => callback(self.error)
)
})
}
// 调用者传进来的函数参数
executor(resolve, reject)
}
实现 then
方法
// Promise.then() 方法的主要作用是
// 将成功或者失败的函数在实例状态
// 还为 pending 的时候赋值给 Promise 实例
MyPromise.prototype.then = function (onFulfilled, onRejected) {
// 如果 调用者 Promise 实例的状态为 pending,
// 则将then方法接收的两个分别处理成功后和失败后
// 的函数赋值给 Promise 实例
if (this.status === PENDING) {
// this.onFulfilled = onFulfilled
// this.onRejected = onRejected
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
} else if (this.status === FULFILLED) {
// 这一步 基本不会被执行
// 如果状态是成功的,直接执行成功的回调函数,
// 并将成功的值传入
console.log('then里面成功的函数')
onFulfilled(this.value)
} else {
// 这一步基本不会被执行
// 如果状态是失败的,直接执行失败的回调函数,
// 并将失败的值传入
console.log('then里面失败的函数')
onRejected(this.error)
}
console.log('this: ', this) // MyPromise 实例
// 这里是一个问题点
// 因为总是会返回第一个 Promise 实例
// 不能实现链式调用 then
return this
}
这里有三个问题点
- 这里的
then
方法返回的是this
,永远都会是第一个调用的Promise
函数实例,所以做不到链式调用 - 如果调用者调用
then
方法的时候并没有传参数,这种情况没有处理。 - 加入
then
方法中的回调函数执行后返回的结果(也就是上面的x)是一个Promise
的实例,直接给resolve
了,这是不希望看到的。
先对3的情况写个处理函数
function resolvePromise(bridgePromise, x, resolve, reject) {
//如果x是一个promise
if (x instanceof MyPromise) {
// 拆解这个 promise ,直到返回值不为 promise 为止
if (x.status === PENDING) {
x.then(y => {
resolvePromise(bridgePromise, y, resolve, reject)}, error => {
reject(error);
});
} else {
x.then(resolve, reject);
}
} else {
// 非 Promise 的话直接 resolve 即可
resolve(x);
}
}
所以改进后的 then
方法为:
MyPromise.prototype.then = function (onFulfilled, onRejected) {
// 首先处理then中的两个参数不传的情况
// 成功回调不传给它一个默认函数
onFulfilled = typeof onFulfilled === 'function' ?
onFulfilled :
value => value
// 对于失败回调直接抛错
onRejected = typeof onRejected === 'function' ?
onRejected :
error => { throw error }
let bridgePromise
let self = this
if (self.status === PENDING) {
return bridgePromise = new MyPromise(
(resolve,reject) => {
self.onFulfilledCallbacks.push((value) => {
try {
// 要拿到 then 中回调返回的结果。
let x = onFulfilled(value)
resolvePromise(bridgePromise, x, resolve, reject)
} catch (e) {
reject(e)
}
})
self.onRejectedCallbacks.push((error) => {
try {
let x = onRejected(error)
resolvePromise(bridgePromise, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
}
上面的只写了进行中的状态下,
还有成功时候(FULFILLED
)的then
方法逻辑:
if (self.status === FULFILLED) {
return bridgePromise = new MyPromise((resolve, reject) => {
try {
// 状态变为成功,会有相应的 self.value
let x \= onFulfilled(self.value);
// 暂时可以理解为 resolve(x),后面具体实现中有拆解的过程
resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e)
}
})
}
如果是失败的状态(REJECTED
)下then
函数逻辑:
if (self.status === REJECTED) {
return bridgePromise = new MyPromise((resolve, reject) => {
try {
// 状态变为失败,会有相应的 self.error
let x \= onRejected(self.error);
resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
})
}
到目前为止,基本上实现了then
方法的链式调用,
现在来测试一下:
// 示例
function MP1 () {
let p1 = new MyPromise(function (resolve, reject) {
let num1 = parseInt(Math.random() * 100)
setTimeout( () => {
if (num1 >= 50) {
resolve(num1)
} else {
reject(num1)
}
}, 3000)
})
return p1
}
function MP2 () {
let p2 = new MyPromise(function (resolve, reject) {
let num2 = parseInt(Math.random() * 1000)
setTimeout(() => {
if (num2 >= 500) {
resolve(num2)
} else {
reject(num2)
}
}, 3000)
})
return p2
}
MP1().then(value => {
console.log('大于或者等于50的num1值为: ', value)
return MP2()
}, error => {
console.log('小于50的num1为: ', error)
return MP2()
})
.then(value => {
console.log('大于或者等于500的num2值为: ', value)
}, error => {
console.log('小于500的num2为: ', error)
})
// (3秒之后) 大于或者等于50的num1值为: 83
// (又过了3秒之后)小于500的num2为: 231
最后,错误捕获及冒泡机制 catch 方法
这个方法实现就简单了,它只是then
方法的语法糖,更重要的是理解其中错误冒泡的机制,即中途一旦发生错误,可以在最后用 catch 捕获错误。
一旦其中有一个PENDING
状态的 Promise
出现错误后状态必然会变为失败
, 然后执行 onRejected
函数,而这个 onRejected
执行又会抛错,把新的 Promise
状态变为失败
,新的 Promise
状态变为失败后又会执行onRejected
........就这样一直抛下去,直到用catch
捕获到这个错误,才停止往下抛。
实现catch方法:
Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected)
}
目前,针对Promise
函数的特点都做了基本的实现,Promise
函数还有一些方法,比如:
- Promise.prototype.finally()
- Promise.all()
- Promise.race()
- Promise.resolve()
- Promise.reject()
- Promise.try
- ....
暂时就没有做进一步实现了