共计 2173 个字符,预计需要花费 6 分钟才能阅读完成。
单线程定义
单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。单线程就是进程只有一个线程。多线程就是进程有多个线程。
Javascript 单线程
其实这里 Javascript 的单线程运行机制和上面单线程定义是一个执行流程,它只有一个主线任务,比如:前面有一条河中间只有一根木棍,想要过河只能从一边按顺序走,单个单个的执行,如果从两头一起过河 Javascript 的单线程就会造成阻塞,这里只是举例了一个简单的解释示例,虽然 JavaScript 是单线程的且效率太低只能按部就班的一步步走,可是浏览器内部不是单线程的。你的一些 I / O 操作、定时器的计时和事件监听(click, keydown...)等都是由浏览器提供的其他线程来完成的
任务队列和事件循环
-
调用栈
代码在运行过程中,会有一个叫做调用栈的概念。调用栈是一种栈结构, 它用来存储计算机程序执行时候其活跃子程序的信息。(比如什么函数正在执行,什么函数正在被这个函数调用等等信息)
代码示例
function expale(a) {return a+1} console.log(expale(1))
上面的例子我们创建了一个简单的函数,然后利用 console 打印并执行了这个函数,在 console 这里执行的时候已经形成了一个栈,我们也可以比喻成一个比较大的盒子,console.log(expale(1))就是底部的一个盒子,然后 expale(1)会变成另一个盒子落在 console.log(expale(1))上面这就形成了栈,其实就是上面所说到的存储计算机程序执行时候其活跃子程序的信息。最后只想说因为它是单线程
-
任务队列
宏任务队列:setTimeout setInterval setImmediate I/O UI rendering(浏览器渲染)微任务队列:process.nextTick(下一个事件轮询的时间点上执行)Promise Object.observer MutationObserver(监视 DOM 变动的接口)console.log('11221121'); // 微任务 Promise.resolve().then(() => {console.log('p 1'); }); // 宏任务 setTimeout(() => {console.log('setTimeout'); }, 0);
等宏任务执行完(全局执行完)就会开始执行整个微任务队列
- 事件循环
如上图所示 stack 存储着要执行的任务,下面黄色的 onclick 函数就是消息队列,一旦有异步响应就会被推入到该队列中。比如:用户的点击事件,setTimeout 等等,然后再进入到 stack 栈中,然后再有任务进入消息队列,再执行这样就形成了事件循环
单线程从任务队列中读取任务是不断循环的,每次栈被清空后,都会在任务队列中读取新的任务,如果没有新的任务,就会等待,直到有新的任务,这就叫任务循环。因为每个任务都由一个事件所触发,所以也叫事件循环。
异步机制
如果函数是异步的,发出调用之后,马上返回,但是不会马上返回预期结果。调用者不必主动等待,当被调用者得到结果之后会通过回调函数主动通知调用者,上面咱们也简单的介绍了栈和任务队列,以及简单的事件循环,做了一些铺垫
代码
function send(){ajax()...}
send()
上面是一段 ajax 的一段代码执行,这时候单线程里面已经有这个任务了,当我们执行 send()之后这个任务在页面中其实已经完成了,但是我们无法拿到它的结果,在 JavaScript 中通过回调函数在耗时操作执行完成后把相应的结果信息传递给回调函数,通知执行 JavaScript 代码的线程执行回调,这也就应上面那句话“不会马上返回预期结果”
浏览器常驻线程
- 渲染引擎线程:顾名思义,该线程负责页面的渲染
- JS 引擎线程:负责 JS 的解析和执行
- 定时触发器线程:处理定时事件,比如 setTimeout, setInterval
- 事件触发线程:处理 DOM 事件
- 异步 http 请求线程:处理 http 请求
在这里我们要注意 js 引擎和渲染线程是不能同时进行的,渲染要在 js 引擎之前
代码
setTimeout(function(){console.log('timer a');
}, 0)
for(var j = 0; j < 5; j++){console.log(j);
}
setTimeout(function(){console.log('timer b');
}, 0)
console.log('click begin');
setTimeout 的作用是操作者可以设定时间插队,我们可以把它想象成一个会员,有特殊待遇,你可以插队,但是这里的 setTimeout(fn, 0)是将函数插入执行队列,等待执行,但是它不会立即执行,不是立即执行,下面的代码就是一个很好的例子 b a
setTimeout(function() {console.log("a")
}, 0)
console.log("b")
总结
其实 JavaScript 单线程并不孤单(浏览器的其他线程帮助它),它只是执行栈内的同步任务,执行完之后会再次执行下一个,直到执行完为止,然后栈中无任务事件,它就会等待直到它出现,一直这样的循环下去
参考文章
JavaScript 异步机制详解
JavaScript 单线程和异步机制
阮老师 Event Loop