先来看几个问题
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 | setTimeout(function(){ |
解析:
setTimeout 是宏任务 把它放到宏任务的队列里
遇到 new Promise 直接执行 输出 “马上执行for循环啦”
遇到then方法,是微任务,把它放到微任务的队列里
遇到console.log 直接执行,输出 “代码执行结束”
本轮宏任务执行完毕,去执行微任务,微任务队列里有then方法的函数,输出 “执行then函数啦”
本轮event loop 执行完毕
下一轮的循环里,发现宏任务队列里有setTimeout 函数,输出 “定时器开始啦”
浅谈setTimeout
这段setTimeout代码什么意思?一般说: 3秒后,会执行setTimeout里的那个函数
1 | setTimeout(function(){ |
但是这种说法并不严谨,正确的解释是:3秒后,setTimeout里的函数会被放到事件队列(event queue)里。而事件队列里的任务,只要在主线程空闲时才会执行。也就是说,如果主线程执行了10秒,那这个任务就会在10+3秒后执行