先来看几个问题
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
栗子
代码语言:javascript复制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里的那个函数
代码语言:javascript复制setTimeout(function(){
console.log('执行了')
},3000)
但是这种说法并不严谨,正确的解释是:3秒后,setTimeout里的函数会被放到事件队列(event queue)里。而事件队列里的任务,只要在主线程空闲时才会执行。也就是说,如果主线程执行了10秒,那这个任务就会在10 3秒后执行
JS 在浏览器端的运行机制 - 小鑫の随笔:https://cloud.tencent.com/developer/article/1945049