初窥JavaScript事宜机制的完成(一)—— Node.js事宜驱动完成概览

在浏览器中,事宜作为一个极为重要的机制,赋予JavaScript响运用户操纵与DOM变化的才能;在Node.js中,事宜驱动模子则是其高并发才能的基本。

进修JavaScript也须要相识它的运转平台,为了更好的明白JavaScript的事宜模子,我盘算从Node及浏览器引擎源码入手,剖析其底层完成,并将我的剖析整顿为一系列博文;一方面作为笔记,另一方面也愿望能与人人交换,剖析和明白有疏漏偏颇的地方,还望列位指正。

简述事宜驱动模子

诠释JavaScript事宜模子自身的好文章已很多了,能够说这已是一个说烂了的话题,这里我只简朴写一下,而且供应一些好文章的链接。

顺序怎样响应事宜

我们的顺序响应外部的事宜有以下两种体式格局:

  • 中缀
    操纵体系处置惩罚键盘等硬件输入就是经由过程中缀来举行的,这个体式格局的优点是纵然没有多线程,我们也能够放心肠实行我们的代码,CPU收到中缀信号今后自动地转去实行响应的中缀处置惩罚顺序,处置惩罚完成后会恢复本来的代码的实行环境继承实行。这类体式格局须要硬件的支撑,一般来说都会被操纵体系封装起来。

  • 轮询
    轮回检测是不是有事宜发作,假如有就去实行响应的处置惩罚顺序。这在底层和上层的开辟中都有运用。
    Windows窗口顺序就须要在主线程中写下以下代码,一般称做音讯轮回:

    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    音讯轮回不断检测是不是有音讯(用户的UI操纵、体系音讯等)涌现,有的话就分发音讯,挪用响应的回调函数举行处置惩罚。
    轮询体式格局的一个瑕玷就是:假如在主线程的音讯轮回里举行耗时操纵,顺序就没法实时响应新的音讯。这在JavaScript中表现显著,今后还会提到这一点,并议论其解决方案。

    但是JavaScript中并没有相似音讯轮回代码,我们只是简朴地注册事宜,然后守候被挪用。这是由于浏览器、Node作为实行平台,已将event loop完成了,JavaScript代码不须要参与到这个过程当中,只须要作为被挪用者安静地守候即可。

相干议论

  1. 知乎-关于浏览器处置惩罚事宜的题目?匿名用户的回复:这个回复里图很不错,有助于明白event loop的事情道理;答案末端有一些文章分享;

  2. MDN – Concurrency model and Event Loop:MDN上对event loop的引见。

Node中的event loop

经由过程Node源码看event loop的完成

Node采纳V8作为JavaScript的实行引擎,同时运用libuv完成事宜驱动式异步I/O。其事宜轮回就是采纳了libuv的默许事宜轮回。

在src/node.cc中,

Environment* env = CreateEnvironment(
        node_isolate,
        uv_default_loop(),
        context,
        argc,
        argv,
        exec_argc,
        exec_argv);

这段代码建立了一个node实行环境,能够看到第三行的uv_default_loop(),这是libuv库中的一个函数,它会初始化uv库自身以及个中的default_loop_struct,并返回一个指向它的指针default_loop_ptr
今后,Node会载入实行环境并完成一些设置操纵,然后启动event loop:

bool more;
do {
  more = uv_run(env->event_loop(), UV_RUN_ONCE);
  if (more == false) {
    EmitBeforeExit(env);
    // Emit `beforeExit` if the loop became alive either after emitting
    // event, or after running some callbacks.
    more = uv_loop_alive(env->event_loop());
    if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0)
      more = true;
  }
} while (more == true);
code = EmitExit(env);
RunAtExit(env);
...

more用来标识是不是举行下一轮轮回。
env->event_loop()会返回之前保存在env中的default_loop_ptruv_run函数将以指定的UV_RUN_ONCE形式启动libuv的event loop。在这类形式下,uv_run会最少处置惩罚一个事宜:这意味着,假如当前事宜队列中没有须要处置惩罚的I/O事宜,uv_run会壅塞住,直到有I/O事宜须要处置惩罚,或许下一个定时器时候到。假如当前没有I/O事宜也没有定时器事宜,则uv_run返回false。

接下来Node会依据more的状况决议下一步操纵:

  • 假如moretrue,则继承运转下一轮loop

  • 假如morefalse,申明已没有守候处置惩罚的事宜了,EmitBeforeExit(env);触发历程的'beforeExit'事宜,搜检并处置惩罚响应的处置惩罚函数,完成后直接跳出轮回。

末了触发'exit'事宜,实行响应的回调函数,Node运转完毕,后面会举行一些资本开释操纵。

在libuv中,event loop会在每次轮回的最先更新本身的time从而完成计时功用,而I/O事宜则分为两类:

  • Network I/O是运用体系供应的非壅塞式I/O解决方案,例如在Linux上运用epoll,windows上运用IOCP。

  • 文件操纵和DNS操纵没有(很好的)体系解决方案,因而libuv自建了线程池,在个中举行壅塞式I/O。

别的我们也能够将自定义的函数抛到线程池中运转,在运转完毕后主线程会实行响应的回调函数,不过Node并没有将这一项功用加入到JavaScript中,也就是说只用原生Node是没法在JavaScript中开启新的线程举行并行实行的。

相干材料

  1. libuv Design Overview:关于libuv的架构及设想思绪;

  2. node child_process ‘exit’:Node的child_process ‘exit’事宜;

  3. Node with threads:议论了运用libuv线程池异步运转JavaScript代码。

下一篇: 初窥JavaScript事宜机制的完成(二)—— Node.js中定时器的完成

    原文作者:Foolyou
    原文地址: https://segmentfault.com/a/1190000002914296
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞