在了解之前先上一个常见的例子



console



以上代码的执行打印出来的结果是怎么样的呢?答案如下



script start
script end
promise1
promise2
setTimeout



如果不理解js的执行机制的话应该比较难取把正确的打印结果回答出来。下面来一步步理解吧。

javascript是一门单线程语言,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果上一个任务的执行过程耗时很长,下一个任务就不得不等待很长的时间,这是设计者就把任务设成了同步任务异步任务。同步任务在主线程上是一个个执行,而异步任务则会进入任务队列(task queue),只有在所有同步任务执行完,异步任务才会被执行。

异步任务有:setTimeout、setlnterval、DOM事件、Promise、Ajax;

到这里可能就会有疑问了上面的代码例子像下图执行才对吗?




javascript 等待中 js 等待执行_js等待异步执行完再执行


这里就要说到宏任务微任务了,宏任务先于微任务执行。宏任务包括整体代码script,setTimeout,setInterval;微任务有Promise,process.nextTick。上面的例子就是整体代码script,所以先执行


javascript 等待中 js 等待执行_任务队列_02


打印出“script start”,再执行到setTimeout属于异步任务,也是宏任务的一种,放到Tasks(宏任务队列)里面


javascript 等待中 js 等待执行_perf script 执行时间_03


接着就执行到Promise也是异步任务,但是属于微任务,进入到Microtask队列


javascript 等待中 js 等待执行_javascript 等待中_04


然后执行到 script的最后打印出“script end”,主线程(JS stack)被清空


javascript 等待中 js 等待执行_任务队列_05


这里要注意的是主线程一旦全部执行完毕就会取清空微任务,所以接下来Microtask中的 Promise then(promise)被加入到主线程中执行


javascript 等待中 js 等待执行_perf script 执行时间_06


执行了回调,打印出“promise1”,同时产生了新的微任务 promise then(promise2),执行完毕后,主线程又被清空了


javascript 等待中 js 等待执行_javascript 等待中_07


微任务队列里还有刚刚产生的 Promise then,又被加入到主线程执行打印出“promise2”


javascript 等待中 js 等待执行_执行settimeout后要清理吗_08


此时微任务队列和主线程都被清空


javascript 等待中 js 等待执行_javascript 等待中_09


接着在宏任务队列里取出一个宏任务加入到主线程中执行 setTimeout callback,打印出“setTimeout”


javascript 等待中 js 等待执行_perf script 执行时间_10


这时主线程、微任务队列、宏任务队列都被清空,代码执行完毕,所以最后的执行结果为script start、script end、promise1、promise2、setTimeout。如果宏任务队列和微任务队列还没清空,就会:主栈全部执行完毕后-->清空微任务-->会取出一个宏任务 --> 执行完毕后-->清空微任务 -> 无线循环,这就是我们所说的事件环(Event Loop),也就是javascript的执行机制。