程序中设置两个线程:一个负责程序本身的运行,称为“主线程”;一个负责主线程和其他线程(各种I/O操作的通信),被称为“Event Loop线程”(消息线程)
1.执行栈和事件队列
当调用一个方法时,js会生成一个与这个方法对应的执行环境(context),又叫执行上下文。这个执行环境中存在着这个方法的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。
当js代码被执行时,因为js是单线程的,同一时间只能执行一个方法。当浏览器第一次加载script时,默认进入了全局执行环境。
- 如果在全局代码中调用了一个函数,那么顺序流就会进入到调用的函数中,创建一个新的执行环境并把这个执行环境添加到执行栈的顶部,一旦函数执行完,它就会被弹出栈的顶部。
- 当解析到异步事件时,js引擎会先将这个事件挂起,丢给WebAPIs(处理DOM事件,http请求,定时器等异步任务)处理返回结果后,会进入到事件队列中,然后等待当前执行栈中的所有任务都执行完毕,主线程处于闲置状态,此时主线程会去查找事件队列中是否有任务,如果有,主线程会取出,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码....遇到异步代码后再执行以上步骤,这就是事件循环机制
2.宏任务(macro task)和微任务(micro task)
因为异步任务之间并不相同,所以异步任务的执行优先级也有区别。异步任务分为两类:宏任务和微任务
- 宏任务:宏任务是JS中最原始的异步任务,包括setTimeout,setInterVal,Ajax等,在代码执行环境中按照同步代码的顺序,逐一进入webAPIs挂起,返回结果后逐一进入异步任务队列,最终按照队列中的顺序进入执行栈中执行
- 微任务:每个宏任务执行之前,程序会先检测是否有未完成的微任务,优先清空当前宏任务中的微任务后再执行下个宏任务
执行顺序:
- 默认的同步代码按照从上到下的顺序运行,运行过程中将本次的微任务和后续的宏任务都挂到webAPIs中返回结果后进入到本次的任务队列中
- 本次同步任务执行完毕后,执行本次任务队列中的微任务,并进入第1步
- 将下一个宏任务开始前的所有微任务执行完毕
- 执行最先进入队列的宏任务,并注册当次的微任务和后续的宏任务,宏任务会按照当前任务队列的队尾继续向下排列