什么是单线程javascript
主程序只有一个线程,即同一时间片段内其只能执行单个任务。html
为何选择单线程?java
JavaScript的主要用途是与用户互动,以及操做DOM。这决定了它只能是单线程,不然会带来很复杂的同步问题。git
单线程意味着什么?github
单线程就意味着,全部任务都须要排队,前一个任务结束,才会执行后一个任务。若是前一个任务耗时很长,后一个任务就须要一直等着。这就会致使IO操做(耗时但cpu闲置)
时形成性能浪费的问题。promise
如何解决单线程带来的性能问题?浏览器
答案是异步!主线程彻底能够无论IO操做,暂时挂起处于等待中的任务,先运行排在后面的任务。等到IO操做返回告终果,再回过头,把挂起的任务继续执行下去。因而,全部任务能够分红两种,一种是同步任务(synchronous),另外一种是异步任务(asynchronous)网络
注: 当主线程阻塞时,任务队列仍然是可以被推入任务的并发
JavaScript 内存模型异步
讲事件循环以前,先看一张下网上看到的 JavaScript 内存模型,相信看完这个会对事件循环机制有一种豁然开朗的感受。
JavaScript 代码执行机制:
Event Loop
如今咱们来聊事件循环。事件循环顾名思义它就是一个循环,主线程会不断循环执行上面的第三步,其基本的代码逻辑以下所示:
while (queue.waitForMessage()) { queue.processNextMessage(); }
常见异步任务进入任务队列时机
行为 | 时机 |
---|---|
DOM操做 | 在用户点击等操做事件完成后 |
网络操做(Ajax等) | 在网络操做响应后 |
定时器 | 在规定时间到达后 |
事件循环机制图解:
MacroTask(Task)
setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering
MicroTask(在ES2015规范中称为Job)
process.nextTick, Promise, Object.observe, MutationObserver
规范:
注: event loop的每一个turn,是由浏览器决定先执行哪一个task queue。这容许浏览器为不一样的task source设置不一样的优先级,好比为用户交互设置更高优先级来使用户感受流畅。
function ELoop() { // 当前任务 let p = new Promise((resolve, reject)=>{ console.log("current Task") resolve(); }); let nextP; setTimeout(()=>{ console.log("MacroTask_1"); nextP.then(()=>{ // 第一次执行时,这段代码并无执行到。 console.log("MicroTask_promise_1"); //第一个MicroTask }) console.log("MacroTask_1 end") }, 0) // 第一个 MacroTask setTimeout(()=>{ console.log("MacroTask_2"); console.log("MacroTask_2 end") }, 0)// 第二个MacroTask nextP = p.then(()=>{ console.log("MicroTask_promise_2"); //第一个MicroTask console.log(1) }).then(()=>{ console.log("MicroTask_promise_3"); // 第二个MicroTask console.log(1) }) console.log("current Task end") } ELoop(); /**输出结果: current Task MicroTask_promise_2 MicroTask_promise_3 MacroTask_1 MicroTask_promise_1 MacroTask_2 **/
参考文献:
从Promise来看JavaScript中的Event Loop、Tasks和Microtasks