前提引入
1. 同步任务与异步任务
所有的任务可以分为同步任务和异步任务。
2. 执行栈与任务队列
执行栈:英文Call Stack,也叫调用栈。
执行栈用于组织JS代码,保障JS代码的有序执行。每当调用一个函数时,都会在执行栈中加入一个与之对应的执行期上下文,这个上下文定义了该函数执行时的环境,加入执行上下文之后,在执行函数。当函数执行后出栈,执行下一个。
任务队列:英文Event Queue,也叫事件队列。
任务队列使用到的是数据结构中的队列结构,它用来保存异步任务,遵循先进先出的原则。它主要负责将新的任务发送到队列中进行处理。
3. macrotask(宏任务队列)与microtask(微任务队列)
任务队列分成macrotask(宏任务队列) 和 microtask(微任务队列)
宏任务: script( 整体代码)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境)
微任务: Promise.then()、Promise.catch()、MutaionObserver、process.nextTick(Node.js 环境);
事件循环
事件循环是JavaScript实现异步的一种方法,也是JavaScript的执行机制。 事件循环又叫消息循环,是浏览器渲染主线程的工作方式。
1.因为 js 是单线程运行的,在代码执行时,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行。先执行同步任务,如果遇到异步任务,js 引擎并不会一直等待其返回结果,而是会将这个任务挂起,交给其他进程或者线程去处理。自己继续执行执行栈中的其他同步任务。
2.当异步任务执行完毕后,再将异步任务对应的回调函数加入到一个任务队列中等待执行。
3.任务队列可以分为宏任务队列 和 微任务队列,当执行栈中的事件执行完毕后,js 引擎首先会判断微任务队列中是否有任务可以执行,如果有,就将微任务队首的事件压入栈中执行。队列遵循先进先出原则。
4.当微任务队列中的任务都执行完成后,再去执行宏任务队列中的任务。
5.如果宏任务队列中有微任务,继续执行微任务。如此反复循环,直至任务队列为空。这就是JavaScript的事件循环机制。
总结JS代码执行顺序:同步任务 => 微任务 => 宏任务。
事件循环执行顺序
也就是说,一次 Event Loop 循环会处理一个 宏任务 和 所有这次循环中产生的微任务。
判断执行顺序可以记住以下几个重点
1、promise中的回调函数立刻执行,then中的回调函数会推入微任务队列中,等待调用栈所有任务执行完才执行
2、async函数里的内容是放入调用栈执行的,await的下一行内容是放入微任务执行的
3、调用栈执行完成后,会不断的轮询微任务队列,即使先将宏任务推入队列,也会先执行微任务
参考: