聊聊Javascript的事件循环

JavaScript、浏览器、事件之间的关系

JavaScript程序采用了异步事件驱动编程(Event-driven programming)模型,维基百科对它的解释是:

事件驱动程序设计(英语:Event-driven programming)是一种电脑程序设计模型。这种模型的程序运行流程是由用户的动作(如鼠标的按键,键盘的按键动作)或者是由其他程序的消息来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由程序员来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在交互程序(Interactive program)的情况下孕育而生的

简而言之,在web前端编程里面JavaScript通过浏览器提供的事件模型API和用户交互,接受用户的输入。

事件驱动程序模型基本的实现原理基本上都是使用 事件循环(Event Loop)。

而JS的运行环境主要有两个:浏览器、Node。

在两个环境下的Event Loop实现是不一样的,在浏览器中基于 规范 来实现,不同浏览器可能有小小区别。在Node中基于 libuv 这个库来实现

JS是单线程执行的,而基于事件循环模型,形成了基本没有阻塞(除了alert或同步XHR等操作)的状态。

浏览器中的事件循环 event loop

先看HTML标准的一系列解释:

为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking)等,用户代理(user agent)必须使用事件循环(event loops)。 有两类事件循环:一种针对浏览上下文(browsing context),还有一种针对worker(web worker)。

为了更好地理解Event Loop,请看下图(转引自Philip Roberts的演讲《Help, I'm stuck in an event-loop》)

上图中,主线程运行的时候,产生堆栈,栈中的代码调用各种外部API,异步操作执行完成后,就在消息队列中排队。只要栈中的代码执行完毕,主线程就会去读取“任务队列”,依次执行那些事件所对应的回调函数。

详细的步骤如下:
  1. 所有同步任务都在主线程上执行,形成一个执行栈
  2. 主线程之外,还存在一个“消息队列”。只要异步操作执行完成,就到消息队列中排队
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会依次读取消息队列的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行
  4. 主线程不断重复上面的的第三步

下面看一个有意思的例子,猜一下它的运行结果:

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

new Promise(
    function(resolve){
        console.log('2');
        resolve()
}).then(
    function(){
        console.log('3');
});

console.log('4');
复制代码

打印结果:

2
4
3
1
复制代码

这是为什么?是不是跟上面说的相违背了?其实这里面就有了两个概念宏任务(task/macrotask),微任务(microtask),下面我们来详细介绍一下这两个东东。

Macrotask 与 Microtask

根据 规范,每个线程都有一个事件循环(Event Loop),在浏览器中除了主要的页面执行线程 外,Web worker是在一个新的线程中运行的,所以可以将其独立看待。

每个事件循环有至少一个任务队列(Task Queue,也可以称作Macrotask宏任务),各个任务队列中放置着不同来源(或者不同分类)的任务,可以让浏览器根据自己的实现来进行优先级排序

以及一个微任务队列(Microtask Queue),主要用于处理一些状态的改变,UI渲染工作之前的一些必要操作(可以防止多次无意义的UI渲染)

主线程的代码执行时,会将执行程序置入执行栈(Stack)中,执行完毕后出栈,另外有个堆空间(Heap),主要用于存储对象及一些非结构化的数据。

常见的macrotask有:

run 
                    
                    

你可能感兴趣的:(javascript,ui,前端,ViewUI)