1. 单线程JavaScript
javascript语言的一大特点就单线程。因此在同一进程上只能运行一个线程;所有任务都需要排队,前一个结束,才会执行后一个任务。
这么设计的主要原因: javascript的主要用途是与用户互动、操作Dom,假如javascript是多线程语言,那么在某个dom节点添加内容时候,同时在这个dom节点删除内容那么就会产生错误,浏览器不知道该以哪个为准了。
2. Event Loop
既然是 js 是单线程,那么我们就难免会遇到单线程难以解决的问题。例如现在在进行一个很大的操作开销。JS 采用了事件循环(event loop) 的机制来解决这个问题。
JavaScript有两种执行方式:
发出调用,立即得到结果是为同步。
发出调用,但无法立即得到结果,需要额外的操作才能得到预期的结果是为异步。
同步就是调用之后一直等待,直到返回结果。异步则是调用之后,不能直接拿到结果,通过一系列的手段才最终拿到结果(调用之后,拿到结果中间的时间可以介入其他任务)。
JavaScript运行环境的运行机制如下
(1)所有任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。系统把异步任务放到"任务队列"之中,然后继续执行后续的任务。
(3)一旦"执行栈"中的所有任务执行完毕,系统就会读取"任务队列"。如果这个时候,异步任务已经结束了等待状态,就会从"任务队列"进入执行栈,恢复执行。
(4)主线程不断重复上面的第三步。
主线程中的所有同步任务执行完毕,再读取任务队列中的异步任务,这个过程是循环不断的。所以,整个的这种运行机制称为Event Loop(事件循环)。
JavaScript运行环境的运行机制示意图如下

3. 宏任务和微任务
根据队列中任务的不同,分为宏任务和微任务。
宏任务(macro-task )常见的有:script、setTimeout、setInterval、I/O、UI render、postMessage、MessageChannel、setImmediate(Node.js 环境)
微任务(micro-task)常见的有:Promise.then、Object.observe、MutationObserver、process.nextTick(Node.js 环境)
Event Loop 的机制中,最基本的一条就是:微任务比宏任务先执行。
微任务和宏任务的执行顺序是先执行同步任务,先执行同步后异步,异步分为宏任务和微任务两种,异步遇到微任务先执行微任务,执行完后如果没有微任务,就执行下一个宏任务。
经典栗子:
代码在执行过程中,遇到异步代码,会将异步代码用队列装起来(挂起),代码中的setTimeout为宏任务,Promise为微任务;
console.info('start')
function fun1(){
console.info('function')
}
fun1();
setTimeout(()=>{
console.info('setTimeout')
},0)
new Promise(resolve => {
console.info('promise')
resolve()
}).then(()=>{
console.info('promise1')
}).then(()=>{
console.info('promise2')
})
console.info('end')

网友评论