事件循环的产生原因
JavaScript是一门单线程的语言,其代码是按顺序执行的,但是随着前端技术不断发展,JavaScript也有一些需要一定时间来执行的代码,例如网络请求,定时器函数等,这得代码需要等待若干时间,会阻塞代码执行,影响浏览器的渲染效率。因此JavaScrip就用事件循环机制和异步操作来实现更高效的代码执行。
事件循环的简介
事件循环(Event Loop):js代码执行时,同步代码直接放入执行栈中执行,异步代码先放入js的运行宿主环境(浏览器或者Node.js),等待异步代码所等待的时间结束后,把异步操作的回调函数根据类型的不同放入宏任务或者微任务的任务队列。
宏任务和微任务的执行顺序为,每次执行完一个宏任务之后,都会检查微任务队列中是否有微任务存在,若微任务队列有微任务存在,则按照微任务队列的队列顺序清空微任务队列,然后再执行下一次宏任务,以此形成一个循环,直到执行完所有代码,(所有宏任务和微任务的执行都是推入执行栈)。
Promise的诞生和使用
异步操作可能会出现嵌套的情况,即一个异步函数的结果的返回值决定下一个异步操作是否执行,例如根据网络请求返回值的不同,决定下一个异步网络请求是否执行,但是回调函数的嵌套可能引发回调函数的嵌套地狱,出现代码的难以阅读和维护,还会难以调试debug,出现错误很难处理,因此产生了promise这种可以链式调用的异步操作方法,避免回调函数的嵌套出现回调函数的地狱。
let promise = new Promise(function(resolve, reject) {
// 异步操作代码
setTimeout(() => {
resolve("操作成功");
}, 1000);
});
promise.then((value) => {
console.log(value); // 输出:操作成功
});
Promise本质是一个构造函数,由一个回调函数作为参数,这个回调函数又有两个函数作为参数,分别为resolve,reject(后文细说两个函数作用)
创建的Promise实例有三个状态分别是pending(初始),fulfilled(成功),rejected(失败),
Promise实例有两个过程,resolved(从初始到成功)和rejected(从初始到失败),promise实例状态一旦变化无法再更改。
Promise中异步操作成功后,调用resolve函数,把promise实例的状态从pending变为fulfilled,并把异步操作成功的结果(如请求返回的数据)作为resolve函数的参数传递出去
Promise中异步操作成功后,调用reject函数,把promise实例的状态从pending变为rejected,并把异步操作失败的结果作为rejected函数的参数传递出去
Promise的实例方法:then、catch、finally
-
then() :
- 作用:为Promise注册成功(fulfilled)和失败(rejected)的回调函数。
- 返回值:返回一个新的Promise对象,实现链式调用。
- 用法:
promise.then(onFulfilled, onRejected)。其中onFulfilled是当Promise成功时调用的函数,onRejected是当Promise失败时调用的函数(可选)。 - 注意:当代码执行到Promise.then时,先把promise.then推入js运行的宿主环境,等待异步操作完成、resolve函数执行后,再把此处的onFulfilled回调函数推入微任务队列
-
catch() :
- 作用:给Promise添加失败(rejected)的回调函数。 - 返回值:返回一个新的Promise对象。
- 用法:
promise.catch(onRejected)。其中onRejected是当Promise失败时调用的函数。
-
finally() :
- 作用:给Promise添加一个事件处理回调函数,无论成功还是失败都会执行。 ** - 返回值:返回一个新的Promise对象。**
- 用法:
promise.finally(finallyCallback)。其中finallyCallback是无论Promise成功还是失败都会执行的回调函数。
Promise的静态方法
Promise还提供了几个静态方法,用于处理多个Promise对象:
- Promise.all() :接受一个数组作为参数,将执行结果以数组形式顺序返回,并发执行数组中的Promise对象。
- Promise.race() :接受一个数组作为参数,返回第一个解决或拒绝的Promise的结果。
- Promise.any() :接受一个数组作为参数,只要数组中有一个Promise成功,就返回那个已经成功的Promise的结果。
- Promise.resolve() :返回一个状态为fulfilled的Promise对象。
- Promise.reject() :返回一个状态为rejected的Promise对象。 以Promise.all()为例
// 模拟异步操作的函数,返回一个 Promise
function asyncOperation(operationName, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${operationName} completed successfully!`);
// 注意:这里没有调用 reject,所以所有操作都会成功
}, delay);
});
}
// 创建多个 Promise 实例
const promise1 = asyncOperation('Operation 1', 1000); // 1秒后成功
const promise2 = asyncOperation('Operation 2', 500); // 0.5秒后成功
const promise3 = asyncOperation('Operation 3', 1500); // 1.5秒后成功
// 使用 Promise.all() 来处理这些 Promise 实例
Promise.all([promise1, promise2, promise3])
.then(results => {
// 如果所有 Promise 都成功,这里会被调用
console.log('All operations completed:', results);
// 输出: All operations completed: [ 'Operation 1 completed successfully!', 'Operation 2 completed successfully!', 'Operation 3 completed successfully!' ]
})
.catch(error => {
// 如果有任何一个 Promise 失败,这里会被调用(但在这个例子中不会被调用)
console.error('An error occurred:', error);
});
// 输出顺序将会是:
// 0.5秒后:'Operation 2 completed successfully!' 的 Promise 解决(但不会被直接打印到控制台)
// 1秒后:'Operation 1 completed successfully!' 的 Promise 解决(同样不会被直接打印)
// 1.5秒后:'Operation 3 completed successfully!' 的 Promise 解决
// 然后,由于所有 Promise 都成功,Promise.all() 的 .then() 会被调用,并打印出所有操作的结果
Promise的链式调用
在链式调用中,每个then方法都会返回一个新的Promise对象。这个新的Promise对象的状态和值取决于上一个Promise对象的状态和值。
function asyncOperation1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Operation 1 completed');
}, 1000);
});
}
function asyncOperation2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Operation 2 completed');
}, 2000);
});
}
function asyncOperation3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Operation 3 completed');
}, 1500);
});
}
asyncOperation1()
.then(result1 => {
console.log(result1);
return asyncOperation2(); //只有asyncOperation1()中的异步操作成功执行了,进而执行resolve函数改变promise实例的状态变成fulfilled,
//才会执行then方法的回调函数,才会返回 return asyncOperation2()进行后续的链式操作
})
.then(result2 => {
console.log(result2);
return asyncOperation3();
})
.then(result3 => {
console.log(result3);
console.log('All operations completed');
})
宏任务和微任务的分类
宏任务:script(包含第一次同步代码的执行),setTimeout,setInterval,setImmediate,I/O,UI-rendering(ui渲染)
微任务:promise.then(),MutationObserver
Async 和 Await
是一个promise的语法糖,他让异步代码的书写看起来更像是同步代码,await之前的都是同步代码,await 阻塞之后的代码,线程跳出当前函数执行其他同步代码,把await之后的函数体部分推入js代码执行宿主环境,等待await异步操作耗时结束,把await之后的这些代码推入微任务任务队列,await之后代码从微任务任务队列推入执行栈执行过程中发现了下一个await,再把await之后的代码推入微任务执行栈,以此类推即可
async function fetchDataSequentially() {
try { //await之前的都是同步代码
const data1 = await fetchSomeData('url1'); await 阻塞代码,等待await异步操作耗时结束,把await之后的代码推入微任务执行栈
console.log('Data 1:', data1);
// 注意:这里会等待 data1 获取完成后才会执行
const data2 = await fetchSomeData('url2', data1); // 可能依赖于 data1 的结果
console.log('Data 2:', data2);
} catch (error) {
// 如果任何一个 await 的 Promise 被拒绝(reject),则这里的 catch 会捕获到错误
console.error('An error occurred:', error);
}
}