事件循环 event loop

// 本文仅用于自己对知识理解和记录

参考文章:http://www.ruanyifeng.com/blog/2014/10/event-loop.html

https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

单线程的JavaScript

单线程一位置JavaScript一次只能做一件事;下一件事要排队;显然我们认为多线程更有效率;同时几个窗口排队办事肯定快;
但是如果是多线程操作,a和b都修改c;a删除了c一个属性而b缺在a的该属性上添加内容;这是以谁的操作为准?
所以单线程反而把复杂的事简单化了

但是单线程上的任务排队进行,只有前一个任务完成才进行下一个任务,当先一个任务耗时很长时后面的任务就停滞了,例如ajax请求要等结果得到才会继续执行;

这时就衍生出了异步任务同步任务

下面是引用阮一峰博客中的总结

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

你不知道的javaScript(中卷)把下面要说的micro task队列叫做任务队列,任务队列也有循环叫做任务循环job loop)甚至可能无限循环导致程序饿死;事件循环event loop)的队列称之为事件循环队列;以事件队列和任务队列区分

事件循环(Event Loop)

事件队列是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程只要执行栈一清空,事件队列上第一位的事件就自动进入主线程。

当我们按下按钮,他的事件处理程序会添加到事件队列8,并在主线程的执行栈清空后立刻读取事件队列*中的第一个事件

这里就要说到定时器(setTimeout)定时器并不是设置100ms就是在100ms后执行代码,而是在100ms后把要执行的代码添加到事件队列。如果在这个时间点上主线程空闲则立即,所以setTimeout是不精确的。

主线程从"事件队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)

任务(事件)和微任务(task 和 micro task)

然而在一次Ladaza的面试中被问到setTimeout和Promise.then的执行顺序时我答砸了,这里捋一下。

看下面的log顺序

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
// promise1
// promise2
// setTimeout

例子中setTimeout在事件队列中加入一个新任务而console.log('script end');是当前正在执行任务(事件)的一部分所以在setTimeout之前打印。

而其中promise.then属于micro task,micro task排在当前事件循环tick结尾处,只要当前任务(事件)执行完成清空所有的排队的micro task;

而setTimeout是调度另一个事件循环tick所以会在promise.then之后执行

排队的micro task也叫做job loop(任务循环)

在浏览器环境中,常见的 macro task 有 setTimeout、MessageChannel、postMessage、setImmediate;常见的 micro task 有 MutationObsever 和 Promise.then。

这里只解释micro task的执行时机,后面结合vue的nextTick做理解。

你可能感兴趣的:(事件循环 event loop)