js是一个单线程的脚本语言。 意味着每次只能去做一件事情,不会存在同时执行另一行代码。
那我们的异步方法又是如何实现的呢?
宏任务和微任务
宏任务(macro task)
- script(整体代码)
- setTimeout
- setInterval
- I/O
- UI交互事件
- postMessage
- MessageChannel
- setImmediate(Node.js 环境)
简单理解就是web api
微任务(micro task)
- Promise.then
- Object.observe
- MutaionObserver
- process.nextTick(Node.js 环境)
就是promise (async/await就是promise的语法糖)
例子
function test(){
console.log('a');
setTimeout(()=>{
console.log('c')
})
console.log('b');
}
一个简单的例子,输出结果是//a b c
首先js在运行代码的时候,会从上到下,逐一解析。
当走到输入a的时候,会将其压入调用栈中(call stack),然后去执行,执行完毕后,再将其从调用栈中移除。
走到定时器的时候,会将其先放入一个异步队列(callback queue),等待同步执行完毕后,再将其压入调用栈执行,所以计时器这块就暂时被挂起,优先输出了下面的b。
输出b的内容完毕后,调用栈清除后发现已经没有内容了。这个时候,调用栈就会去取异步队列的内容去执行。然后输出了c。
function test() {
setTimeout(() => console.log(4));
new Promise(resolve => {
resolve();
console.log(1);
}).then(_ => {
console.log(3);
});
console.log(2);
}
这个例子就是宏任务和微任务的优先级
setTimeout由于是异步,js优先将其挂起,压入callback queue中 ,setTimeout为宏任务挂载到宏任务队列中
new Promise的时候是马上去进行new了一个新对象,为同步代码,优先执行。输出1.此时.then方法为微任务挂载到微任务队列中。
然后继续执行同步代码,输出2.
此时调用栈的内容都执行完毕了,他现在要做的就是要把异步队列中的任务取过来运行。此时我们现在有两个异步队列,分别是宏任务队列中的setTimeout,微任务队列中的.then。微任务的执行顺序是优先与宏任务的。
所以把微任务队列中then方法压入了调用栈执行,输出为3.
微任务执行后,调用栈又没东西了,然后去看宏任务队列中还有没有要执行的。发现宏任务队列中有setTimeout。所以再把setTimeout取过来,压入调用栈执行,输出为4.
为什么说这个是浏览器的事件循环机制,因为js在执行的过程中,先优先执行同步代码。执行完后在去异步队列中看看有没有要执行的,处于一直在轮训的过程中。
DOM是在何时进行渲染的
还是因为js是单线程的,所以在js执行完同步代码后,也会优先去渲染dom,此时是不会去取异步队列的内容执行的。因为渲染的速度也是很快的,然后从感官上不会有因为异步任务而导致页面出现问题的