..

浏览器事件循环

学习 Javascript 的过程中,事件循环是一个绕不开的话题。异步???,事件模型???任务???事件循环???这些都是什么意思?

https://www.w3.org/TR/html5/webappapis.html#event-loops 手册上是这么说的: 事件循环其实是浏览器和事件, 用户交互,渲染,网络请求等等协作的机制, 时间循环有一个或者多个任务队列,这些任务包括解析HTML,处理回调函数,网络请求等等。

![黑人问号](/images/???.jpeg)

在看了Philip Roberts在JSConf上面的session之后:

我好像有点懂了

function foo() {
    console.log("foo");
}

function bar() {
    foo();
}

bar()

可视化 js读取文件,调用函数就压到栈里面,可以执行返回就执行并且退出栈,直到所有都运行完成,很简单。 js在运行的过程中,更确切一些,在栈中有代码需要运行的时候,用户界面是无法响应其他事情的。

回调队列

function foo() {
    console.log("foo");
}

function bar() {
    setTimeout(foo(), 1000);
}

bar()

可视化 setTimeout是浏览器实现的API,他的计时器在计算到了1000ms之后会把回调函数放入事件循环的队列里面,在栈为空的情况下,浏览器会在队列拿出第一个来压到栈里面执行。这就是为什么setTimeout不是严格的1s后执行,而是至少1s后执行。

看看这个例子:

for (var i = 0; i < 10; i++) {
    console.log("Hello ", i);
}

for (var i = 0; i < 10; i++) {
    setTimeout(() => {
        console.log("Hello ", i);
    }, 0);
}

代码想要做的事情都是一样的,打印 Hello 1 - 9, 但实际效果是不同,第一段是持续运行,会阻止用户界面的响应,而第二段代码是把 console.log 放入队列中,这就给了浏览器在运行代码过程中响应用户的机会。 这里还有一个梗,看出来了吗?

再读一遍手册上面的定义,是不是已经理解事件循环了。

![](/images/harden-eye.jpeg)

当然事情并没有那么简单

来看看另外一个例子

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');

我第一次看到这段代码回答就是

script start
script end
setTimeout
promise1
promise2

我这么说肯定这个答案是不对的,不对的原因是什么?

![](/images/not-easy.jpeg)

setTimeout 会把回调函数放入队列里面,作为Task, 而Promise也会放入队列里面,作为Microtask,Microtask比Task拥有更高优先级,在栈为空后要立即执行Microtask,而且如果队列中有Microtask,必须执行完所有的Microtask才能返回去执行Task。 除了Promise,MutationObserver也是Microtask。

Boss战

![灭霸](/images/mieba.jpg)
# HTML
<div class="outer">
  <div class="inner"></div>
</div>

# JS
// 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);

测试

点击绿色的方块查看结果

所以你答对了吗?