javascript运行机制

事件循环

  • JS分为同步任务和异步任务
  • 同步任务都在主线程上执行,形成一个执行栈
  • 主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
  • 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。
    事件循环机机制
    事件循环机制进一步补充:
    事件循环机制

上图大致描述就是:

1)主线程运行时会产生执行栈
2)栈中的代码调用某些api时,它们会在事件队列中添加各种事件(当满足触发条件后,如ajax请求完毕)
3)而栈中的代码执行完毕,就会读取事件队列中的事件,去执行那些回调
4)如此循环
5)注意,总是要等待栈中的代码执行完毕后才会去读取事件队列中的事件

关于定时器思考:
问:当调用setTimeout后, 是如何等待特定时间后才添加到事件队列中的?是JS引擎检测的么?

答:它是由定时器线程控制的, 计时完成后就会将特定的事件推入事件队列中。

宏任务与微任务

除了广义的同步任务和异步任务,进一步,JS中又分为两种任务类型:macrotask和microtask,在ECMAScript中,microtask称为jobs,macrotask可称为task。

它们的定义?区别?简单点可以按如下理解:

macrotask(又称之为宏任务),可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)\

  • 每一个task会从头到尾将这个任务执行完毕,不会执行其它
  • 浏览器为了能够使得JS内部task与DOM任务能够有序的执行,会在一个task执行结束后,在下一个 task 执行开始前,对页面进行重新渲染 (task->渲染->task->…)

microtask(又称为微任务),可以理解是在当前 task 执行结束后立即执行的任务

  • 也就是说,在当前task任务后,下一个task之前,在渲染之前
  • 所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染
  • 也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)

分别是什么样的场景会形成macrotask和microtask呢?

  • macrotask:主代码块,setTimeout,setInterval等(可以看到,事件队列中的每一个事件都是一个macrotask)
  • microtask:Promise,process.nextTick等

再根据线程来理解下:

  • macrotask中的事件都是放在一个事件队列中的,而这个队列由事件触发线程维护
  • microtask中的所有微任务都是添加到微任务队列(Job Queues)中,等待当前macrotask执行完毕后执行,而这个队列由JS引擎线程维护 (感觉上是)

所以,总结下运行机制:

  • 执行一个宏任务(栈中没有就从事件队列中获取)
  • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
  • 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
    js运行机制

看一段示例代码:

setTimeout(function () {
    console.log("setTimeout---5");
})
new Promise(function (resolve, reject) {
    console.log("Promise---1");
    resolve("success");
}).then(function (e) {
    console.log(e+"---3");
    console.log("then---4");
})
console.log("console---2")

运行结果为:

Promise---1
console---2
success---3
then---4
setTimeout---5

再看一段示例代码:

console.log("1");
setTimeout(function(){
    console.log("2");
    new Promise(function(resolve,reject){
        console.log("3");
        resolve("4");
    }).then(function(e){
        console.log(e);
        console.log("5")
    })
})
new Promise(function(resolve,reject){
    console.log("6");
    reject("7");
}).then(function(e){
    console.log(e);
    console.log("don't run here");
},function(e){
    console.log(e);
    console.log("8");
})
setTimeout(function(){
    console.log("9");
    new Promise(function(resolve,reject){
        console.log("10");
        resolve("11");
    }).then(function(e){
        console.log(e);
        console.log("12")
    })
})

运行结果为:

1
6
7
8
2
3
4
5
9
10
11
12
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章