文章目录
Promise的用法
一、什么是Promise ?
- Promise是一个类,可以翻译成 承诺、许诺 、期约;
- 当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个Promise的对象;
- 在通过new创建Promise对象时,我们需要传入一个回调函数,我们称之为executor
这个回调函数会被立即执行,并且给传入另外两个回调函数resolve、reject;
当我们调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数;
当我们调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数;
二、Promise的代码结构
上面Promise使用过程,我们可以将它划分成三个状态:
-
待定(pending): 初始状态,既没有被兑现,也没有被拒绝; 当执行executor中的代码时,处于该状态;
-
已兑现(fulfilled): 意味着操作成功完成; 执行了resolve时,处于该状态;
-
已拒绝(rejected): 意味着操作失败; 执行了reject时,处于该状态
new Promise((resolve, reject) => {
// pending状态: 待定的
//reject("处于rejected状态") // 处于rejected状态(已拒绝状态)
resolve("处于fulfilled状态") // 处于fulfilled状态(已敲定/兑现状态)
}).then(res => {
console.log("res:", res)//res即为resolve里面传进来的参数,处于fulfilled状态
}).catch(err => {
console.log("err:", err)//err即为reject里面传进来的参数,处于rejected状态
})
Executor是在创建Promise时需要传入的一个回调函数,这个回调函数会被立即执行,并且传入两个参数:
通常我们会在Executor中确定我们的Promise状态:
通过resolve,可以兑现(fulfilled)Promise的状态,我们也可以称之为已决议(resolved);
通过reject,可以拒绝(reject)Promise的状态;
这里需要注意:
- 一旦状态被确定下来,Promise的状态会被 锁死,该Promise的状态是不可更改的
- 在我们调用resolve的时候,如果resolve传入的值本身不是一个Promise,那么会将该Promise的状态变成兑现(fulfilled);在之后我们去调用reject时,已经不会有任何的响应了(并不是这行代码不会执行,而是无法改变Promise状态)
三、Promise的对象方法then、catch、finally
then方法:
then方法是Promise对象上的一个方法:它其实是放在Promise的原型上的 Promise.prototype.then
then方法接受两个参数:
- fulfilled的回调函数:当状态变成fulfilled时会回调的函数;
- reject的回调函数:当状态变成reject时会回调的函数
一个Promise的then方法是可以被多次调用的:
- 每次调用我们都可以传入对应的fulfilled回调;
- 当Promise的状态变成fulfilled的时候,这些回调函数都会被执行;
catch方法:
catch方法也是Promise对象上的一个方法:它也是放在Promise的原型上的 Promise.prototype.catch。 一个Promise的catch方法是可以被多次调用的:
- 每次调用我们都可以传入对应的reject回调;
- 当Promise的状态变成reject的时候,这些回调函数都会被执行;
promise.catch(err=>
{console.log(err)})
promise.catch(err=>
{console.log(err)})
finally方法:
- finally是在ES9(ES2018)中新增的一个特性:表示无论Promise对象无论变成fulfilled还是reject状态,最终都会被执行的代码。
- finally方法是不接收参数的,因为无论前面是fulfilled状态,还是reject状态,它都会执行
const promise = new Promise((resolve, reject) => {
// resolve("resolve message")
reject("reject message")
})
promise.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
}).finally(() => {
console.log("finally code execute")//都会被执行
})
四、Promise的类方法resolve、reject、all、allSettled、race、any方法
前面我们学习的then、catch、finally方法都属于Promise的实例方法,都是存放在Promise的prototype上的。
下面我们再来学习一下Promise的类方法:
resolve:
Promise.resolve的用法相当于new Promise,并且执行resolve操作:
Promise.resolve("cy")//等价于
new Promise((resolve,reject)=>{
resolve("cy")}
)
reject
reject方法类似于resolve方法,只是会将Promise对象的状态设置为reject状态
Promise.reject的用法相当于new Promise,只是会调用reject
Promise.reject("cy")//等价于
new Promise((resolve,reject)=>{
rejecct("cy")}
)
all
Promise.all:
它的作用是将多个Promise包裹在一起形成一个新的Promise;
新的Promise状态由包裹的所有Promise共同决定:
- 当所有的Promise状态变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值组成一个数组;
- 当有一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(22222)
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 3000);
})
// 所有的Promise都变成fulfilled时, 再拿到结果
// 意外: 在拿到所有结果之前, 有一个promise变成了rejected, 那么整个promise是rejected
Promise.all([p2, p1, p3, "aaaa"]).then(res => {
console.log(res)
}).catch(err => {
console.log("err:", err)//22222
})
allSettled
在ES11(ES2020)中,添加了新的API Promise.allSettled:
该方法会在所有的Promise都有结果(settled),无论是fulfilled,还是reject时,才会有最终的状态;
并且这个Promise的结果一定是fulfilled的:
allSettled的结果是一个数组,数组中存放着每一个Promise的结果,并且是对应一个对象的;
这个对象中包含status状态,以及对应的value值;
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(22222)
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 3000);
})
// allSettled
Promise.allSettled([p1, p2, p3]).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
// [
// { status: 'fulfilled', value: 11111 },
// { status: 'rejected', reason: 22222 },
// { status: 'fulfilled', value: 33333 }
// ]
race
race是竞技、竞赛的意思,表示多个Promise相互竞争,谁先有结果,那么就使用谁的结果
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 3000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(22222)
}, 500);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 1000);
})
// race: 竞技/竞赛
// 只要有一个Promise变成fulfilled状态, 那么就结束
Promise.race([p1, p2, p3]).then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
// err: 22222
any:
any方法是ES12中新增的方法,和race方法是类似的:
any方法会等到一个fulfilled状态,才会决定新Promise的状态;
如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态
如果所有的Promise都是reject的,那么会报一个AggregateError的错误
注意:如果所有的Promise都是reject的,那么会报一个AggregateError的错误,可以通过err.error获取reject传入的参数
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve(11111)
reject(1111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(22222)
}, 500);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve(33333)
reject(3333)
}, 3000);
})
// any方法
Promise.any([p1, p2, p3]).then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err.errors)
})
手写实现Promise
大致思路:
首先.因为promise是一个类。所以我们在手写实现promise时,也要新建一个类,promise在使用时,通常会传入一个回调函数,参数为resolve、reject,并且由于这个回调函数是立即执行的,所以我们可以在类的construct里面实现resolve、reject函数,在类的内部实现then方法。其他方法这里暂时不做实现。由于promise有三个状态,所以我们定义三个变量区分这三个状态,初始时为pending.另外由于resolve、reject传入的参数会在then和catch的回调函数里面作为他们的参数,因此我们需要用2个变量分别保存他们。由于会有多个then连接的情况还会有多次抛出错误的情况,我们使用2个数组存储这些回调。
constructor里面实现resolve、reject函数
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
// 工具函数,捕获异常用
function execFunctionWithCatchError(execFn, value, resolve, reject) {
try {
const result = execFn(value)
resolve(result)
} catch(err) {
reject(err)
}
}
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then方法:
then(onFulfilled, onRejected) {
return new HYPromise((resolve, reject) => {
// 1.如果在then调用的时候, 状态已经确定下来
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
// try {
// const value = onFulfilled(this.value)
// resolve(value)
// } catch(err) {
// reject(err)
// }
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
// try {
// const reason = onRejected(this.reason)
// resolve(reason)
// } catch(err) {
// reject(err)
// }
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2.将成功回调和失败的回调放到数组中
if (this.status === PROMISE_STATUS_PENDING) {
this.onFulfilledFns.push(() => {
// try {
// const value = onFulfilled(this.value)
// resolve(value)
// } catch(err) {
// reject(err)
// }
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
this.onRejectedFns.push(() => {
// try {
// const reason = onRejected(this.reason)
// resolve(reason)
// } catch(err) {
// reject(err)
// }
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
})
}
}
总代码包含测试代码:
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
// 工具函数
function execFunctionWithCatchError(execFn, value, resolve, reject) {
try {
const result = execFn(value)
resolve(result)
} catch(err) {
reject(err)
}
}
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
return new HYPromise((resolve, reject) => {
// 1.如果在then调用的时候, 状态已经确定下来
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
// try {
// const value = onFulfilled(this.value)
// resolve(value)
// } catch(err) {
// reject(err)
// }
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
// try {
// const reason = onRejected(this.reason)
// resolve(reason)
// } catch(err) {
// reject(err)
// }
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2.将成功回调和失败的回调放到数组中
if (this.status === PROMISE_STATUS_PENDING) {
this.onFulfilledFns.push(() => {
// try {
// const value = onFulfilled(this.value)
// resolve(value)
// } catch(err) {
// reject(err)
// }
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
this.onRejectedFns.push(() => {
// try {
// const reason = onRejected(this.reason)
// resolve(reason)
// } catch(err) {
// reject(err)
// }
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
})
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
// resolve(1111) // resolved/fulfilled
reject(2222)
// throw new Error("executor error message")
})
// 调用then方法多次调用
promise.then(res => {
console.log("res1:", res)
return "aaaa"
// throw new Error("err message")
}, err => {
console.log("err1:", err)
return "bbbbb"
// throw new Error("err message")
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})