JS单线程事件执行顺序,宏任务与微任务

399 阅读2分钟

1. JS代码的执行顺序

JS引擎执行代码是是单线程,同一时刻只会有一个进程执行JS代码,多任务需要排队。

JavaScript的单线程,与它的用途有关,作为浏览器脚本语言,JavaScript的主要用途是与用户交互,以及操作DOM。这决定了它只能是单线程,否则会带来很多复杂的同步问题。这种模式可能会阻塞代码,导致代码执行效率低下。

为了规避这些问题,出现了异步任务,用回调函数的方式来实现异步代码的存放于执行。

将所有的任务分为两种:同步任务异步任务

同步任务:在主线程上排队执行任务,只有前一个任务执行完毕,才能执行下一个任务

异步任务:不进入主进程,而进入任务队列,在主进程空闲时,事件触发线程会从任务队列取出一个任务(即异步的回调函数)放入主进程中执行。

2. 为什么会有异步

如果代码只能自上而下执行,如果上一行解析时间很长,那么下面的代码就会被阻塞。用户就会想"这么卡,算了不用了",这样就导致了很差的用户体验。而使用异步编程就会将任务添加到任务队列中,优先执行同步代码,将回调函数或者比较难加载的放到队列中,就会节省很多时间。

3. 任务队列的执行顺序

异步函数中还有微任务与宏任务

宏任务:整体代码script、setTimeOut、setInterval、BOM/DOM交互事件

微任务:promise的resolve/reject、promise.then、process.nextTick(node)、MutaionObserver

先微任务microtask队列,再宏任务macrotask队列 。

所以:同步任务=>异步任务微任务=>宏任务

例如:
console.log('event start')

setTimeout(function () {
    console.log('setTimeout');
});

new Promise(function(resolve,reject){
    console.log('promise start')
    resolve()
}).then(function(){
    console.log('promise end')
})

console.log('event end')

//执行结果:
event start
promise start
event end
promise end
setTimeout

注意:new Promise是会进入到主线程中立刻执行,而promise.then则属于微任务。

4.简单练习

console.log(1) 
setTimeout(function(){ 
    console.log(2) 
},0) 
console.log(3)

执行结果:1 3 2
//关于异步的:
setTimeout(()=>{
    console.log('setTimeout1')
},0)

let p = new Promise((resolve,reject)=>{
    console.log('Promise1')
    resolve()
    console.log('Promise2')
})

p.then(()=>{
  console.log('Promise3')    
})

执行结果:
Promise1 
Promise2 
Promise3 
setTimeout1
console.log('event 1')

Promise.resolve().then(() => {
  setTimeout(() => {
    console.log('setTimeout 1')
  }, 0)
  console.log('Promise 1')
}).then(() => {
  console.log('Promise 2')
})

console.log('event 2')

setTimeout(() => {
  console.log('setTimeout 2')
}, 0)

执行结果:
event 1
Promise 1
event 2
Promise 2
setTimeout 2
setTimeout 1