1、Promise的基本概念
1.1、基本概念:
Promise
是JavaScript
中的一种用于处理异步操作的对象
。- 可以把它想象成一个“承诺”——程序承诺会在未来的某个时间点完成某件事(比如从服务器获取数据),但是,并不立即返回结果。
1.2、Promise的状态:
Promise 有三种状态:
Pending(进行中)
: 承诺还没有完成,也没有失败,它正在等待某件事情发生。Fulfilled(已完成)
: 承诺成功地完成了,并且返回了一个结果。Rejected(已失败)
: 承诺未能完成,发生了某种错误或问题。
1.3、生活场景示例:
假设小明让小红帮忙买咖啡,小红答应了,这时,这个“答应”的过程就是一个
Promise
,它可能有三种状态:
- Pending:小红还在咖啡店排队,咖啡还没买到手。
- Fulfilled:小红已经买到咖啡,并且把咖啡给了小明。
- Rejected:小红因为某种原因没法买到咖啡,告诉小明:“抱歉,没买到”。
2、Promise的链式调用规则
Promise的
链式调用
是指:
- 在一个
Promise
的.then() 方法
中返回一个新的Promise,然后可以继续在这个新的Promise上调用.then() 方法
。- 这种方式可以:让我们可以把多个异步操作串联起来,依次处理,避免回调地狱。
.then()
返回的是一个新的Promise
:- 每次调用
.then() 方法
后,你都会得到一个新的Promise
。 - 这使得你可以在下一个
.then()
里继续处理上一个.then()
的结果。
- 每次调用
.then() 的回调函数
可以返回值:- 如果
回调函数
返回的是一个普通的值(而不是 Promise),这个值会被包装成一个已经完成的 Promise,并传递给下一个.then()
。
- 如果
- 回调函数返回的是
Promise
:- 如果回调函数返回的是一个
Promise
,那么下一个.then()
将会等待这个Promise
完成,然后继续处理结果。
- 如果回调函数返回的是一个
错误传递
:- 如果任何一个
.then()
中发生错误(包括抛出异常或返回一个被拒绝的 Promise),这个错误会被传递到后面最近的.catch()
中处理。
- 如果任何一个
3、Promise的静态方法
Promise.resolve(value)
:- 用来快速创建一个已经完成的
Promise
,并将给定的value
作为结果。 - 适用于:将一个普通的值或非Promise的结果
转换为Promise
- 用来快速创建一个已经完成的
Promise.reject(reason)
:- 用来快速创建一个被拒绝的Promise,并将给定的 reason 作为错误原因。
- 适用于:将一个错误或失败的结果转换为 Promise
Promise.all(iterable)
:- 接受一个可迭代对象(通常是一个数组)作为参数,其中每个元素都是一个 Promise。
- 只有当所有的 Promise 都完成时,返回的 Promise 才会被完成,结果是一个包含所有结果的数组
- 如果有任何一个Promise被拒绝,Promise.all会立即返回一个被拒绝的Promise
Promise.race(iterable)
:- 接受一个可迭代对象,其中每个元素也是一个 Promise。
- 返回的 Promise 会在第一个 Promise 完成或拒绝时,采用这个 Promise 的结果或错误。
- 适用于:你只关心最先完成的异步操作的场景
Promise.allSettled(iterable)
:- 接受一个可迭代对象,每个元素是一个 Promise。
- 返回的 Promise 会等待所有的 Promise 都结束(无论是完成还是拒绝),并返回一个数组,数组中的每一项都是一个对象,表示每个 Promise 的状态和结果。
- 适用于:你希望等待所有 Promise 结束后,处理每个 Promise 的结果
Promise.any(iterable)
:- 接受一个可迭代对象,每个元素是一个 Promise。
- 返回的 Promise 会在第一个成功(fulfilled)的 Promise 完成时被完成。如果所有的 Promise 都被拒绝,返回的 Promise 会被拒绝,错误信息是所有错误的一个数组。
- 适用于:你只关心最先成功的异步操作的场景
4、async和await
async
和await
是JavaScript
中用于处理异步操作的关键字
。- 它们让你能够像写同步代码那样编写异步代码,使得代码更加清晰和易读。
4.1、async 函数
async
是用来定义一个异步函数
的关键字
。- 一个函数一旦被定义为 async,它就会自动返回一个 Promise,而不论你在函数中实际返回的是什么。
4.2、await 关键字
await
只能在async函数
内部使用。- 它用于等待一个
Promise
完成,并获得Promise解决的值
- 简单理解:
- 使用
await
,就像在告诉JS
:“等一下,等这个Promise
完成了,再继续执行后面的代码。”
- 使用
4.3、async 和 await 的好处:
- 更易读:它让你可以像写同步代码一样写异步代码,不需要再用
.then()链式调用
,代码逻辑更直观 - 错误处理:你可以使用
try...catch语句
来处理异步操作中的错误,这样比处理Promise
的.catch()
更加清晰。
5、事件循环中,Promise进入的是哪些事件队列
以下:包含一些常规的事件队列总结
setTimeout的回调
:宏任务(macro task)setInterval的回调
:宏任务(macro task)promise
的then函数回调
:微任务(micro task)requestAnimationFrame的回调
:介于宏任务和微任务之间,但通常被归类为宏任务(macro task)事件处理函数
:宏任务(macro task)
6、面试例题
根据下列代码,判断代码输出的结果是什么:
// 下面代码的输出结果是什么 => 先后打印:1、2、4、3const promise = new Promise((resolve, reject) => {console.log(1);resolve();console.log(2);});promise.then(() => {
// 这里不先打印`3`是因为:promise里的then回调会进入"微任务队列",它必须等待全局(同步)代码执行完成后,再执行微队列里的代码console.log(3); });console.log(4);
// 下面代码的输出结果是什么 => 1、5、2、3、4const promise =new Promise((resolve,reject)=>{// 1)同步代码先执行 => 打印1console.log(1);// 2)延时器进入宏队列,等待处理// 5)处理宏队列任务setTimeout(()=>{// 5-1、打印输出2console.log(2);// 5-2、将Promise对象状态改为成功状态 => 回调函数进入微队列,但,需要等到当前宏队列任务完成后才能执行微队列任务resolve();// 5-3、打印输出3console.log(3);}); });// 3)因为promise对象此时还是pending状态,没有满足成功状态,此时这里代码也是不执行的,也不会放入微队列promise.then(()=>{// 6)处理微队列任务 => 打印输出4console.log(4);});// 4)执行同步代码 => 打印5console.log(5);
// 下面代码的输出结果是什么const promise1 = new Promise((resolve, reject)=>{setTimeout(()=>{resolve()},1000)})const promise2 = promisel.catch(()=>{return 2;})
// 因为:它们还在等待`resolve`或`reject`状态// 1)打印:promise1 Promise{<pending>} // 2)打印:promise2 Promise{<pending>}console.log('promise1', promise1)console.log('promise2', promise2)setTimeout(()=>{
// 因为:`promise1`被`resolve`后,状态变为`fulfilled`,且`resolve()`没有传递值,所以`fulfilled`的值是`undefined`。// 3)打印:promise1 Promise{<undefined>}// 4)打印:promise2 Promise{<undefined>}console.log('promise1', promise1)console.log('promise2',promise2)},2000)
// 下面代码的输出结果是什么 => 0、2、1async function m(){// 1)立即执行,打印 0console.log(0)// 2)遇到await,m函数则暂停执行,将返回的1包装成一个已解决的Promise,这时控制权返回到主线程,继续执行同步代码const n = await 1;// 4)等待获取l的结果后再执行console.log(n);}m();// 3)同步代码立即执行,打印2console.log(2);
// 下面代码的输出结果是什么 => 0、3、1、2async function m(){// 1)立即执行,打印0console.log(0)// 2)await表达式会立即执行(即 Promise.resolve(1)),但,会让出执行线程const n = await 1;// 5)在微任务队列中执行,await完成后打印 1console.log(n);}(async ()=>{// 4) 调用 m(),进入 m 函数,暂停在 await 处await m();// 6) 在 m() 执行完后,打印 2console.log(2);})();// 3) 同步代码立即执行,打印 3 console.log(3);