概述
JavaScript 中的 Promise 是一种用于处理异步操作的对象,它提供了一种更清晰和可管理的方式来处理异步代码。Promise 用于处理诸如网络请求、文件读取、定时任务等异步操作,以便更好地控制流程和处理错误。以下是 Promise 的基本概念和用法:
-
Promise 的状态:
- Promise 可以处于三种状态之一:
pending
(待定)、fulfilled
(已成功)、rejected
(已拒绝)。 - 初始状态是
pending
,然后可以转换为fulfilled
或rejected
。
- Promise 可以处于三种状态之一:
-
创建一个 Promise:
要创建一个 Promise,你可以使用
new Promise()
构造函数,并传入一个带有两个参数的函数,通常称为执行器函数(executor function)。这个函数接受两个参数:resolve
和reject
,它们是两个回调函数,用于在异步操作完成时分别标志成功和失败。const myPromise = new Promise((resolve, reject) => { // 异步操作 if (/* 操作成功 */) { resolve(result); // 成功时调用 resolve } else { reject(error); // 失败时调用 reject } });
-
处理 Promise:
你可以使用
.then()
方法来处理 Promise 的成功状态,以及.catch()
方法来处理失败状态。这些方法接受回调函数作为参数,这些回调函数在 Promise 完成时被调用。myPromise .then(result => { // 处理成功情况 }) .catch(error => { // 处理失败情况 });
-
Promise 链:
你可以使用
.then()
方法构建 Promise 链,每个.then()
返回一个新的 Promise,可以在其上进行更多的操作。这使得异步操作可以按照顺序执行,使代码更具可读性。myPromise .then(result => { // 第一个异步操作的处理 return anotherPromise; // 返回一个新的 Promise }) .then(result => { // 第二个异步操作的处理 }) .catch(error => { // 处理任何错误 });
-
Promise.all():
Promise.all()
是一个实用工具,它接受一个 Promise 数组,并在所有 Promise 都成功完成时触发一个回调。如果其中任何一个 Promise 失败,则整个 Promise.all() 将失败。const promises = [promise1, promise2, promise3]; Promise.all(promises) .then(results => { // 所有 Promise 都成功时执行 }) .catch(error => { // 如果任何一个 Promise 失败,将触发此处 });
-
Promise.race():
Promise.race()
与Promise.all()
类似,但它在第一个 Promise 解决(无论是成功还是失败)时触发回调,而不是等待所有 Promise 完成。const promises = [promise1, promise2, promise3]; Promise.race(promises) .then(result => { // 第一个完成的 Promise 触发 }) .catch(error => { // 如果第一个失败的 Promise 触发 });
优势
- 可以避免回调地狱:将回调函数转换成了链式调⽤,代码可读性更好。
- 可以⽀持多个并发请求:Promise.all() 可以让多个 Promise 并⾏执⾏,提⾼了执⾏效率。
- 可以在异步代码中捕获错误:Promise.catch() 可以捕获异步代码中的错误。
使用场景
- Ajax 请求:Promise 可以⽤于异步获取数据,当数据请求成功时,调⽤ Promise 的 resolve ⽅法,否则调⽤ reject ⽅法。
- 定时器:Promise 可以结合定时器⼀起使⽤,实现定时器的延时操作。
- 图⽚加载:Promise 可以⽤于图⽚的异步加载,当图⽚加载成功时,调⽤ Promise 的 resolve ⽅法,否则调⽤ reject ⽅法。
- 优化回调函数:将回调函数转换成 Promise 链,提⾼代码可读性。
- 实现并发请求:Promise.all() 可以让多个请求并⾏执⾏。
- 解决回调地狱:将嵌套的回调函数转换成链式调⽤,提⾼代码可读性。
使用总结
ES6 中的 Promise 是⼀种⽤于处理异步操作的机制,它可以更加优雅地处理异步操作,避免了回调地狱的问题。在 JavaScript 中,Promise 对象代表⼀个异步操作的最终状态(成功或失败),并且可以链式调⽤。
Promise 对象提供了 then ⽅法,可以将异步操作成功的回调函数和失败的回调函数注册到 Promise 对象上,当 Promise 对象状态变为 Fulfilled 或 Rejected 时,会⾃动调⽤对应的回调函数。then ⽅法中的回调函数并不是⽴即执⾏,⽽是放⼊微任务队列中,在 JavaScript 主线程执⾏栈为空时,才会被调⽤执⾏。
Promise构造函数接受⼀个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不⽤⾃⼰部署。
resolve函数的作⽤是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为
resolved),在异步操作成功时调⽤,并将异步操作的结果,作为参数传递出去;reject函数
的作⽤是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在
异步操作失败时调⽤,并将异步操作报出的错误,作为参数传递出去。
Promise实例⽣成以后,可以⽤then⽅法分别指定resolved状态和rejected状态的回调函数,即then 的两个参数,第⼀个回调函数是Promise对象的状态变为 resolved时调⽤,第⼆个回调函数是Promise对象的状态变为rejected时调⽤,且第二个回函数参数是可选的。 then⽅法返回的是⼀个新的Promise实例。
此外,Promise 还提供了其他的方法:
- catch ⽅法:捕获 Promise 对象的错误信息,相当于 then(null, onRejected)。
- finally ⽅法:⽆论 Promise 对象状态如何,finally ⽅法总是会执⾏,通常⽤于资源清理等操作。
- Promise.all ⽅法:接收⼀个 Promise 对象数组,当所有 Promise 对象状态都变为
Fulfilled 时,返回⼀个 Promise 对象,状态为 Fulfilled,并携带所有 Promise 对象的结
果;当有任意⼀个 Promise 对象状态变为 Rejected 时,返回的 Promise 对象状态变为 Rejected,并携带第⼀个 Promise 对象的错误信息。 - Promise.race ⽅法:接收⼀个 Promise 对象数组,当其中任意⼀个 Promise 对象状态变为 Fulfilled 或 Rejected 时,返回⼀个 Promise 对象,状态为对应 Promise 对象的状态,并携带对应 Promise 对象的结果或错误信息。
练习题
如何顺序执行 10 个异步任务?
function asyncTask1() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("Async task 1");
resolve();
}, 1000);
});
}
function asyncTask2() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("Async task 2");
resolve();
}, 2000);
});
}
// 顺序执⾏异步任务
asyncTask1()
.then(() => {
return asyncTask2();
})
.then(() => {
// 执⾏完异步任务1和异步任务2后的逻辑
});
或者换一种写法:
function fn1() {
return new Promise((resolve, reject) => {
console.log('fn1执⾏')
setTimeout(() => {
console.log('fn1结束')
resolve('fn1传递过去的参数')
}, 1000)
})
}
function fn2(data) {
return new Promise((resolve, reject) => {
console.log('fn2执⾏,接收的参数', data)
setTimeout(() => {
resolve('fn2传递过去的参数')
}, 1000)
})
}
function fn3(data) {
return new Promise((resolve, reject) => {
console.log('fn3执⾏,接收的参数', data)
setTimeout(() => {
resolve('fn3传递过去的参数')
}, 1000)
})
}
fn1().then(fn2).then(fn3).then(res => {
console.log('最后⼀个', res)
})
或者使用 async 和 await :
async function runAsyncTasks() {
await asyncTask1();
await asyncTask2();
// 执⾏完异步任务1和异步任务2后的逻辑
}
runAsyncTasks();
当然还可以使用生成器:
function* main() {
const res1 = yield fn1("开始");
const res2 = yield fn2(res1);
const res3 = yield fn3(res2);
console.log(res3, "全部执⾏完毕");
}
const task = main();
task.next();
function fn1(data) {
setTimeout(() => {
console.log("fn1执⾏", data);
task.next("fn1执⾏完毕");
}, 1000);
}
function fn2(data) {
setTimeout(() => {
console.log("fn2执⾏", data);
task.next("fn2执⾏完毕");
}, 1000);
}
function fn3(data) {
setTimeout(() => {
console.log("fn3执⾏", data);
task.next("fn3执⾏完毕");
}, 1000);
}
console.log("我是最开始同步执⾏的");