js运行机制
众所周知,js是一门单线程的语言。主要同他的用途有关,同一个时间只能做一件事儿,作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM,这决定了它只能是单线程,否则会带来很复杂的同步问题。
js事件循环
由于javaScript的资源加载是按序进行的,javaScript的单线程决定只有一个任务结束才可以执行下一个任务,否则只能是下一个任务处于等待状态。而任务分为同步任务与异步任务:
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务:不进入主线程、而进入"任务队列"的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行;
通常我们会将网页的Dom渲染作为同步任务,将获取远程数据,图片加载等操作作为异步任务进行。
js任务执行顺序
- 全部的同步任务会进入主线程,异步任务进入Event Table并注册函数,Event Table会将这个函数移入Event Queue。
- 主线程任务执行完毕后,js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数,进入主线程执行。
- 以上两步会循环进行
setTimeout,promise以及async
队列任务优先级:
promise.Trick() > promise的回调 > async > setTimeout > setInterval
宏任务与微任务的区分:
宏任务:包括整体代码script,setTimeout,setInterval
微任务:Promise,process.nextTick
事件循环的顺序
事件循环的顺序决定js代码的执行顺序,进入整体代码(宏任务)后,开始第一次循环。所有的同步任务先执行,根据任务类型部分异步任务进入入Event Queue,部分异步任务进入不同的Event Queue,宏任务的第一次循环结束后接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。
面试题
console.log('start')
async function async1() {
console.log("async1");
await async2();
console.log("async1");
}
async function async2() {
console.log( 'async2');
}
console.log("script");
setTimeout(function () {
console.log("setTimeout");
},0);
async1();
new Promise(function () {
console.log("promise1");
}).then(function () {
console.log("promise2");
});
复制代码
输出:
start
script
async1
async2
promise1
promise2
async1
setTimeout
复制代码
解析: 首先执行所有的同步任务,async与await搭配使用时是异步任务,async单独定义函数时则当做同步任务执行,所以输出start,script,遇到setTimeout将其放在入Event Queue任务队列中,执行async1函数后 输出async1,async2,此时要等待async2执行完毕,将其放在微任务的Event Queue中,所以此时主线程执行下面的代码,new Promise实例会立即执行,输出promise1,碰到then异步函数进入Event Queue任务队列中,此时宏任务第一轮执行完毕,去执行所有的微任务,微任务执行结束后将宏任务的任务队列的异步任务放在主线程进行执行。