JS 在浏览器端的运行机制

先来看几个问题

1、JS为什么是单线程?

JS的单线程,与他的用途有关。作为浏览器脚本语言,Js的主要用途就是与用户互动,以及操作DOM。这决定了它只能是单线程。

试想一下,假设现在有process1、process2两个线程,process1在某个DOM节点上添加了内容,process2删除了这个节点,那这时浏览器应该以哪个线程为准呢?

所以,为了避免复杂性,JS从诞生起就是单线程

2、为什么需要异步任务?

既然JS是单线程,那么所有的任务就得排队,一个个执行,假如上一条任务执行了很久,那么后面的任务就会被阻塞。这个现象对于用户而言,就是 “页面卡死”,用户体验极差。所以,JS需要异步任务。

几个知识点

执行栈

所有同步任务都在主线程上执行,形成一个执行栈,执行栈是存储函数调用的栈结构,遵循先进后出的原则

任务队列

只要异步任务有了运行结果,就在 “任务队列” 中放置一个事件

任务类型

任务类型可分为:

  • macro-task(宏任务):包括整体代码script、setTimeout、setInterval
  • micro-task(微任务):Promise、process.nextTick

Event Loop

禅 - 小鑫

栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
setTimeout(function(){
console.log('定时器开始啦')
});

new Promise(function(resolve){
console.log('马上执行for循环啦');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('执行then函数啦')
});

console.log('代码执行结束');

解析:

setTimeout 是宏任务 把它放到宏任务的队列里

遇到 new Promise 直接执行 输出 “马上执行for循环啦”

遇到then方法,是微任务,把它放到微任务的队列里

遇到console.log 直接执行,输出 “代码执行结束”

本轮宏任务执行完毕,去执行微任务,微任务队列里有then方法的函数,输出 “执行then函数啦”

本轮event loop 执行完毕

下一轮的循环里,发现宏任务队列里有setTimeout 函数,输出 “定时器开始啦”

浅谈setTimeout

这段setTimeout代码什么意思?一般说: 3秒后,会执行setTimeout里的那个函数

1
2
3
setTimeout(function(){
console.log('执行了')
},3000)

但是这种说法并不严谨,正确的解释是:3秒后,setTimeout里的函数会被放到事件队列(event queue)里。而事件队列里的任务,只要在主线程空闲时才会执行。也就是说,如果主线程执行了10秒,那这个任务就会在10+3秒后执行