JS同步异步宏任务微任务

2,292 阅读4分钟

JS同步异步宏任务微任务

同步异步

js是单线程语言(主线程)

可以把线程看做一个人在做事,突然间觉得渴了但是水没有烧,所有先烧水,但是烧水的过程不是一瞬间就烧好了,像这个过程算是一个比较耗时的任务了。但是另外一件事情还正在做着。

这里我们就可以把烧水这个事情看做是异步,正在做的事情是同步。

主线程是把执行栈中的代码取出来依次执行,当解析器执行的时候会先查看当前要执行的代码是同步的还是异步的,如果是同步的话会放入执行栈主线程去执行,如果是异步会放入Event Table中,当异步任务完成后会放入到事件队列中Event Queue中,这个里面存储的就是异步任务完成后该做的事情,等主线程把执行栈里面的代码执行完成后,这时相当于主线程空闲起来了,这时主线程就会读取异步队列Event Queue里面的函数,如果有就取出来放到主线程中去执行,执行完成后查看事件队列Event Queue中是否还有异步任务,如果有继续执行。

注意:new Promise() 是同步方法,resolve才是异步方法。

具体异步执行

首先进去主程序main.js 判断是否有异步事件,如果没有就直接退出程序。如果有,就进入事件循环Event Loop 不断的循环整个流程。如果异步任务也没有了就会退出。

Timer 处理所有setInterval setTimeout 的回调,这个过程会查看定时器的时间到了没,如果到了就把对应的函数执行了,如果没有就跳过这个执行下一个过程。

I/O (一般为ajax,读文件等io读写).

​ Pending I/O Callback

​ 执行I/O 回调、文件操作、网络操作等 是否完成如果完成就执行它

​ Idle,Prepare

​ 内部使用(这个可以不用考虑)

​ Poll

​ 轮询I/O 操作,是否有I/O callback ,如果没有 就会产生阻塞一段时间(有超时和基本检测)

在轮询I/o阶段会首先看check阶段有没有任务有的话就进入check阶段(就不轮询做阻塞了)如果没有check任务,就会阻塞等待ajax的返回,如果超过了定的一个时间也会跳入下一个阶段。如果在这个轮询的阶段发生了ajax的回调的话就会回到Padding阶段执行,然后又会进入到Poll轮询,如果这个阶段又发生了ajax的回调就再回到Padding阶段执行。所以这个I/O阶段是一个循环阶段

Check 只处理 setImmediate 的回调函数

Close Callbacks 专门处理一些close 类型的回调,如关闭网络连接等


这个过程完成后会判断Event Loop 中是否有事件没有完成,如果没有就退出,如果有则再进入Timer -> I/O -> check -> close Callbacks 再次执行循环,直到整个任务中没有任何的异步任务,就会退出

宏任务微任务

宏任务(macrotask):script(整体代码)、setTimeout、setInterval、UI渲染、I/O、postMessage、messageChannel、setImmediate(node.js环境)

微任务(microtask):Promise、MutaionObserver、process.nextTick(node.js环境)

注意:new Promise() 是宏任务,then内部是微任务

在代码执行的过程中,被推倒主线程的代码又分成了宏任务和微任务,宏任务先执行然后在执行同步代码中的微任务


**总结:**首先确认代码是同步还是异步,同步代码进入执行栈在主线程执行,执行完成主线程的同步代码后看异步任务,在异步任务中根据Timer -> I/O -> check -> close Callbacks 过程执行,每执行一步会推一段代码到主线程中,推倒主线程之后判断是宏任务还是微任务,宏任务和微任务区分开去执行(不管是什么代码最后都是在执行栈中由主线程去执行--单线程)。


console.log(1);
setTimeout(() => {
    console.log(2);
}, 1000)

new Promise(resolve => {
    console.log(3);
    setTimeout(() => {
        console.log(4);
    }, 3000)
    resolve()
}).then(res => {
    console.log(5)
}).then(res => {
    console.log(6);
})
setTimeout(() => {
    new Promise(resolve => {
        resolve()
        console.log(7);
    }).then(res => {
        console.log(8)
    })
}, 2000)
setTimeout(() => {
    console.log(9);
}, 3000)
console.log(10);

//1,3,10,5,6,2,7,8,4,9