javascript是一门单线程语言
将任务分为两类:
- 同步任务
- 异步任务
打开网站时,网页的渲染过程就是一大堆同步任务,比如页面骨架和页面元素的渲染。而像加载图片音乐之类占用资源大耗时久的任务,就是异步任务。
同步的进入主线程,异步的进入Event Table并注册函数。当指定的事情完成时,Event Table会将这个函数移入Event Queue。主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的Event Loop(事件循环)。
js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。
举些例子:
1、setTimeout
1 | console.log('script start') //1. 打印 script start |
输出顺序:script start->script end->settimeout
2、Promise
Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,打印p的时候,是打印的返回结果,一个Promise实例。
1 | console.log('script start') //1. 打印 script start |
// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout
3、async await
1 | async function async1(){ |
// 输出顺序:script start->async1 start->async2->script end->async1 end
同时有setTimeout、Promise、Async/Await ,执行顺序又是什么?引入
- macro-task(宏任务):包括整体代码script,setTimeout,setInterval
- micro-task(微任务):Promise,process.nextTick
进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。
1 | setTimeout(function() { |
// 输出顺序:promise->console->then->setTimeout
- 先遇到setTimeout,那么将其回调函数注册后分发到宏任务Event Queue。
- 接下来遇到了Promise,new Promise立即执行,then函数分发到微任务Event Queue。
- 遇到console.log(),立即执行。
- 整体代码script作为第一个宏任务执行结束,发现了then在微任务Event Queue里面,执行。
- 第一轮事件循环结束了,从宏任务Event Queue开始第二轮循环。发现了宏任务EventQueue中setTimeout对应的回调函数,立即执行。
- 结束。
再来分析两个案例:
1 | console.log('1'); //1. 打印 1 |
// 输出顺序:1,7,6,8,2,4,3,5,9,11,10,12
1 | async function async1() { |
// 输出顺序:script start->async1 start->async2->promise1->script end->promise2->async1 end->settimeout