事件循环
JavaScript 的事件循环(Event Loop)是 JavaScript 运行时环境(如浏览器和 Node.js)用来处理异步代码和回调的一种机制。它允许 JavaScript 在执行长时间运行的操作(如 I/O、网络请求等)时不会阻塞用户界面或程序的其余部分。
事件循环的基本工作原理
- 调用栈(Call Stack) :JavaScript 代码执行时,会按照调用顺序将函数放入调用栈中。当函数执行完毕,它会被从栈中移除。
- Web APIs:浏览器提供了一系列的 Web APIs,如
setTimeout,fetch,XMLHttpRequest等,用于执行异步操作。这些 API 通常在“后台”线程中运行,以避免阻塞主线程。 - 任务队列(Task Queue) :当异步操作完成时,相应的回调函数会被添加到任务队列中。这个队列是先进先出(FIFO)的。
- 事件循环:当调用栈为空时,事件循环会从任务队列中取出任务,并将它们放入调用栈中执行。这个过程会不断重复,形成事件循环。
- 微任务队列(Microtask Queue) :除了任务队列外,JavaScript 还有一个微任务队列。微任务队列的优先级高于任务队列。在每次事件循环迭代结束时,JavaScript 引擎会先处理所有微任务队列中的任务,然后再处理任务队列中的任务。
JavaScript可以大致分为同步任务和异步任务
异步任务又分为宏任务与微任务 一般先执行宏任务再执行任务(先宏后微)
宏任务
微任务
console.log(1);
setTimeout(()=>{
console.log(2);
},0)
new Promise((resolve,reject)=>{
console.log('new Promise');
resolve()
}).then(()=>{
console.log('then');
})
console.log(3);
/**
输出
1
new Promise
3
then
2
解析:
1、经过 console.log(1); 所以打印 '1'
2、定时器属于新的宏任务,等待上一个宏任务和微任务执行完后才会执行
3、new Promise 直接执行 所以打印 ' new Promise '
4、.then 属于微任务,放入微任务队列,等待宏任务执行完后再执行
5、经过 console.log(3); 所以打印 '3'
6、执行玩宏任务=>执行微任务(即执行打印 'then')
7、当第一次的宏任务和微任务执行完后执行下一次任务即执行定时器 打印 '2'
**/
async与 await
async 和 await 是 JavaScript(ES2017 引入)中用于处理异步操作的关键字,它们让异步代码的编写和阅读变得更加直观和简单。在使用这些关键字之前,处理异步操作通常涉及到回调函数、Promises 或者生成器(Generators)。
async
async关键字用于声明一个异步函数,这意呀着该函数内部可以有异步操作。- 使用
async声明的函数会隐式地返回一个 Promise。即使你没有在函数体中显式地返回一个 Promise,JavaScript 引擎也会将函数的返回值(无论是普通值还是其他类型的值)包装在一个 Promise 中返回。 async函数中可以使用await关键字。
await
await关键字只能在async函数内部使用。await用于等待一个 Promise 完成,并返回其解决(resolved)的值。- 当执行到
await表达式时,JavaScript 引擎会暂停当前async函数的执行,等待 Promise 完成(无论是解决还是拒绝),然后继续执行async函数并返回解决的值(如果 Promise 被解决)。如果 Promise 被拒绝,则会抛出一个错误,需要用 try...catch 来捕获。 await可以让你以同步的方式写异步代码,提高了代码的可读性和可维护性。
async函数返回一个promise对象,下面的两种方法是等效的
function Fn(){
return Promise.resolve('TEST');
}
async function asyncFn() {
return 'TEST'
}
正常情况下,
await命令后是一个Promise对象,返回该对象的结果。如果不是 Promise对象,就直接返回对应的值
async function fff() {
return await 123
}
fff().then(v => console.log(v))
不管
await后面跟着的是什么,await都会阻塞后面的代码
async function awiatfn1() {
console.log(1);
await fn2()
console.log(2); //阻塞 属于微任务等待宏任务执行完再执行
}
async function awaitfn2() {
console.log('fn2');
}
awiatfn1()
console.log(3);
/*
输出
1
fn2
3
2
*/
综合流程分析案例
async function asyncfn1() { //宏任务
console.log('async1 start');
await asyncfn2()
console.log('async1 end'); //阻塞(后面执行) 属于微任务
}
async function asyncfn2() { //宏任务
console.log('async2');
}
console.log('script start'); //宏任务
setTimeout(function(){
console.log('setTimeout'); //定时器下一个宏任务
})
asyncfn1()
new Promise(function (resolve){ //宏任务
console.log('promise1');
resolve()
}).then(function(){
console.log('promise2'); //阻塞(后面执行) 属于微任务
})
console.log('script end'); //宏任务
/*输出
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
解析:
先执行宏任务再执行微任务
1、先后执行的宏任务
console.log('script start');=>asyncfn1();=>asyncfn2()=>new Promise=>console.log('script end');
2、先后执行的微任务
console.log('async1 end');=>console.log('promise2');
3、下一个宏任务
console.log('setTimeout');
*/