众所周知,为了与浏览器进行交互,Javascript是一门非阻塞单线程脚本语言。
为何单线程? 因为如果在DOM操作中,有两个线程一个添加节点,一个删除节点,浏览器并不知道以哪个为准,所以只能选择一个主线程来执行代码,以防止冲突。虽然如今添加了webworker等新技术,但其依然只是主线程的子线程,并不能执行诸如I/O类的操作。长期来看,JS将一直是单线程。
为何非阻塞?因为单线程意味着任务需要排队,任务按顺序执行,如果一个任务很耗时,下一个任务不得不等待。所以为了避免这种阻塞,我们需要一种非阻塞机制。这种非阻塞机制是一种异步机制,即需要等待的任务不会阻塞主执行栈中同步任务的执行。这种机制是如下运行的:
执行栈(execution context stack)
任务队列(task queue)
。任务队列
,任务队列中的异步任务(即之前等待任务的回调结果)会塞入主执行栈,事件循环(Event Loop)
用一张图展示这个过程:
在实际情况中,上述的任务队列(task queue)
中的异步任务分为两种:微任务(micro task)
和宏任务(macro task)
。
Promises(浏览器实现的原生Promise)
、MutationObserver
、process.nextTick
setTimeout
、setInterval
、setImmediate
、I/O
、UI rendering
script(整体代码)
即一开始在主执行栈中的同步代码本质上也属于macrotask,属于第一个执行的taskmicrotask和macotask执行规则:
下面来个简单例子:
console.log(1); setTimeout(function() { console.log(2); }, 0); new Promise(function(resolve,reject){ console.log(3) resolve() }).then(function() { console.log(4); }).then(function() { console.log(5); }); console.log(6);
一步一步分析如下:
再来一个复杂的例子:
// Let‘s get hold of those elements var outer = document.querySelector(‘.outer‘); var inner = document.querySelector(‘.inner‘); // Let‘s listen for attribute changes on the // outer element new MutationObserver(function() { console.log(‘mutate‘); }).observe(outer, { attributes: true }); // Here‘s a click listener… function onClick() { console.log(‘click‘); setTimeout(function() { console.log(‘timeout‘); }, 0); Promise.resolve().then(function() { console.log(‘promise‘); }); outer.setAttribute(‘data-random‘, Math.random()); } // …which we‘ll attach to both elements inner.addEventListener(‘click‘, onClick); outer.addEventListener(‘click‘, onClick);
假设我们创建一个有里外两部分的正方形盒子,里外都绑定了点击事件,此时点击内部,代码会如何执行?一步一步分析如下:
原文:https://www.cnblogs.com/planetwithpig/p/11681071.html