前言
如果一个函数的执行结果需要另一个函数的执行结果,而这一个函数的执行结果还需要另一个函数的执行结果这种情况一直嵌套下去该如何处理?举个实际的例子:我们需要从一个api获取用户信息,然后基于用户id从另一个api获取用户的订单列表,最后根据订单中的商品id获取商品详情。ps:每个步骤都是异步的,并且后一步骤依赖于前一步骤的结果。
由于js是单线程语言,在es6之前解决上述问题就需要用到回调函数callback(),将函数传入另一个函数调用,然后把这个函数传入下一个函数调用,使下一个函数能获取到这个函数的执行结果进行调用....如此循环往复,一步步的嵌套不仅使代码变得晦涩难懂、不优雅,还会使得维护的成本过高,要是其中哪一个函数计算出错,就会导致最后的结果南辕北辙。我们称这种用回调函数不断嵌套下去来模拟异步需求的行为为回调地狱。
于是,在es6中,promise出现了。
基础知识
Event Loop
JavaScript 的事件循环机制,同步任务会立即执行,异步任务会被放入任务队列中。在事件循环中,当主线程的同步任务执行完毕后,会先处理所有微任务队列中的任务,然后再处理宏任务队列中的任务。
基本原理
1.单线程执行:JS是单线程的,对于UI渲染和响应用户交互来说,可以避免多线程环境中的数据竞争和同步问题
2.任务队列:Event Loop的核心思想是将认为有分为两类:宏任务和微任务。宏任务包括setTimeout,setInterval,I/O渲染,UI渲染等,微任务包括promise回调,process.nextTick等,每当有任务完成,其回调会被放入相应的任务队列
3.执行流程:
- JS首先执行全局脚本代码,这是当前的宏任务
- 执行过程中,遇到异步操作,将回调函数安排到相应的微任务队列
- 当前宏任务执行完成后,Event Loop会检查微任务队列,如果有待处理的微任务,则全部执行这些微任务
- 清空微任务队列后,执行下一个宏任务,重复上述操作。
4.实际案例:
console.log('Start');
setTimeout(() => {
console.log('Timer 1');
}, 0);
Promise.resolve().then(() => {
console.log('Promise 1');
});
setTimeout(() => {
console.log('Timer 2');
}, 0);
Promise.resolve().then(() => {
console.log('Promise 2');
});
console.log('End');
分析执行过程:
-
宏任务: 全局脚本开始执行,打印
'Start'
和'End'
。 -
微任务: 在宏任务执行过程中,遇到
Promise
,它们的.then()
回调函数会被添加到微任务队列中。这里我们有两个Promise
调用,所以两个.then()
回调函数会被安排在微任务队列中。 -
宏任务结束: 宏任务执行完所有的同步代码后,接下来是
setTimeout
的回调函数,由于延迟时间为 0,它们会在当前宏任务之后作为新的宏任务执行,但在此之前,所有的微任务必须先执行完毕。 -
执行微任务: 所有的微任务在当前宏任务结束后立即执行,因此
Promise 1
和Promise 2
的回调函数会按顺序执行并打印。 -
执行下一个宏任务: 当微任务队列清空后,事件循环会继续并执行下一个宏任务,这里是
setTimeout
的回调函数,打印'Timer 1'
和'Timer 2'
。 -
最终执行结果:
Start End Promise 1 Promise 2 Timer 1 Timer 2
相关概念
- 异步非阻塞:Event Loop确保即使有长时间运行的异步操作(如文件读写、网络请求),也不会阻塞主线程,因此UI可以保持响应。
- 微任务优先:相比于宏任务,微任务总是在当前宏任务结束后立即执行,这使得微任务适合于需要快速响应的场景。
Promise
promise是JS的一种处理异步操作的方法,它提供一种标准简易的方式处理异步代码,优于传统的回调函数。promise代表一个异步操作的最终完成(或失败)及其结果值,有三种状态:
1.pending(等待中):初始状态,既没有完成也没有失败
2.fulfilled(已成功):表示操作完成,此时promise变为resolved,并且带有成功的结果值。
3.rejected(已失败):表示操作失败,此时promise变为rejected,并带有错误的原因。
基本用法
创建一个Promise实例通常使用new Promise((resolve, reject) => {...})
构造函数,其中resolve
和reject
是两个函数,分别用于改变Promise的状态。
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const result = true;
if (result) {
resolve('操作成功');
} else {
reject('操作失败');
}
}, 2000);
});
// 使用then处理成功情况,catch处理失败情况
myPromise
.then(result => console.log(result))
.catch(error => console.error(error));
链式调用
Promise支持链式调用,即.then()
方法可以返回一个新的Promise,使得多个异步操作可以按顺序执行
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
let result = true
if (result)
resolve("第一个操作成功")
else
reject("第一个操作失败")
}, 1000)
}
)
myPromise
.then(result => {
console.log(result);
return new Promise((resolve, reject) => {
setTimeout(() => {
let result = false
if (result)
resolve("第二个操作成功")
else
reject("第二个操作失败")
}, 1000)
});
})
.then(secondResult => console.log(secondResult))
.catch(error => console.error(error));
Promise.all()
当需要并行执行多个Promise并等待它们全部完成时,使用Promise.all()
方法。
const promise1 = Promise.resolve('Promise 1');
const promise2 = new Promise((resolve) => setTimeout(resolve, 500, 'Promise 2'));
const promise3 = new Promise((resolve, reject) => setTimeout(reject, 900, 'Promise 3 failed'));
Promise.all([promise1, promise2, promise3])
.then(results => console.log(results)) // 不会被调用,因为其中一个Promise被reject了
.catch(error => console.error(error)); // 输出: "Promise 3 failed"
Promise.race()
如果只需要等待其中任何一个Promise完成(无论成功或者失败),可以使用Promise.race()方法
const promise1 = Promise.resolve('Promise 1');
const promise2 = Promise.resolve('Promise 2');
const promise3 = Promise.reject('Promise 3');
Promise.race([promise1, promise2, promise3])
.then(result => console.log(result)) // 可能是任何一个先完成的Promise的结果
.catch(error => console.error(error)); // 如果第一个完成的是rejected的Promise,则会捕获到错误
then()
基本用法
每当创建一个 Promise实例时,会传入一个 executor 函数,该函数接收两个参数:resolve和reject,分别用于标记 Promise 成功(fulfilled)或失败(rejected)。一旦 Promise的状态从 pending(等待中)变为 fulfilled 或 rejected,其关联的 then 方法就会被调用。then方法接受两个参数:第一个是处理成功情况的回调函数(onFulfilled),第二个是处理失败情况的回调函数(onRejected),这两个参数都是可选的。
链式调用
then方法可以链式调用,使得多个异步操作可以按顺序执行,每个then的返回值可以传递给下一个then.
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("第一步完成");
}, 1000);
});
promise
.then(result => {
console.log(result); // 输出: "第一步完成"
return "第二步完成";
})
.then(nextResult => {
console.log(nextResult); // 输出: "第二步完成"
})
.catch(error => {
console.error(error);
});
//cathch用于捕获整个Promise链中的错误,它等同于在每个 then 中添加错误处理回调
参考文档:https://juejin.cn/post/7392541545659547683