前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >nodejs事件循环阶段之prepare

nodejs事件循环阶段之prepare

作者头像
theanarkh
发布2020-03-12 00:21:01
7840
发布2020-03-12 00:21:01
举报
文章被收录于专栏:原创分享

prepare是nodejs事件循环中的其中一个阶段(phase)。属于比较简单的一个阶段。我们知道libuv中分为handle和request。而prepare阶段的任务是属于handle。我们看一下他的定义。

在这里插入图片描述
在这里插入图片描述

下面我们看看怎么使用它

代码语言:javascript
复制
void prep_cb(uv_prepare_t *handle) {
    printf("Prep callback\n");
}

int main() {
    uv_prepare_t prep;
    // uv_default_loop是libuv事件循环的核心结构体
    uv_prepare_init(uv_default_loop(), &prep);
    uv_prepare_start(&prep, prep_cb);
    uv_run(uv_default_loop(), UV_RUN_DEFAULT);
    return 0;
}

执行main函数,libuv就会在prepare阶段执行回调prep_cb。我们分析一下这个过程。

代码语言:javascript
复制
int uv_prepare_init(uv_loop_t* loop, uv_prepare_t* handle) {              
    uv__handle_init(loop, (uv_handle_t*)handle, UV_PREPARE);                   
    handle->prepare_cb = NULL;                                                 
    return 0;                                                                 
  } 

1 uv__handle_init是初始化libuv中handle的一个通用函数,他主要做了下面几个事情。

1 初始化handle的类型,所属loop 2 打上UV_HANDLE_REF,该标记影响事件循环的退出和poll io阶段超时时间的计算。具体在start函数的时候分析。 3 handle插入loop->handle_queue队列的队尾,每个handle在init的时候都会插入libuv的handle队列。

2 初始化prepare节点的回调。 init函数主要是做一些初始化操作。我们继续要看start函数。

代码语言:javascript
复制
 int uv_prepare_start(uv_prepare_t* handle, uv_prepare_cb cb) {           

       // 如果已经执行过start函数则直接返回
    if (uv__is_active(handle)) return 0;                                      
    if (cb == NULL) return UV_EINVAL;                                         
    QUEUE_INSERT_HEAD(&handle->loop->prepare_handles, &handle->queue);         
    handle->prepare_cb = cb;                                                   
    uv__handle_start(handle);                                                 
    return 0;                                                                 
  }   

1 设置回调,把handle插入loop中的prepare_handles队列,prepare_handles保存prepare阶段的任务。在事件循环的prepare阶段会逐个执行里面的节点的回调。

2 设置UV_HANDLE_ACTIVE标记位,如果这handle还打了UV_HANDLE_REF标记(在init阶段设置的),则事件循环中的活handle数加一。UV_HANDLE_ACTIVE标记这个handle是活的,影响事件循环的退出和poll io阶段超时时间的计算。有活的handle的话,libuv如果运行在默认模式下,则不会退出,如果是其他模式,会退出。 执行完start函数,libuv的结构体大概如下。

然后我们看看libuv在事件循环的prepare阶段是如何处理的。

代码语言:javascript
复制
 void uv__run_prepare(uv_loop_t* loop) {                                      
    uv_prepare_t* h;                                                         
    QUEUE queue;                                                              
    QUEUE* q;                                                                 

    /*
        把该类型对应的队列中所有节点摘下来挂载到queue变量,
        相当于清空prepare_handles队列,因为如果直接遍历prepare_handles队列,
        在执行回调的时候一直往prepare_handles队列加节点,会导致下面的while循环无法退出。
        先移除的话,新插入的节点在下一轮事件循环才会被处理。
    */                            
     QUEUE_MOVE(&loop->prepare_handles, &queue);    
   // 遍历队列,执行每个节点里面的函数
    while (!QUEUE_EMPTY(&queue)) {                                            

      // 取下当前待处理的节点,即队列的头
      q = QUEUE_HEAD(&queue);                                                 

      // 取得该节点对应的整个结构体的基地址,即通过结构体成员取得结构体首地址
      h = QUEUE_DATA(q, uv_prepare_t, queue);                                

      // 把该节点移出当前队列
      QUEUE_REMOVE(q);                                                        

     // 重新插入原来的队列
      QUEUE_INSERT_TAIL(&loop->prepare_handles, q);                            

     // 执行回调函数
      h->prepare_cb(h);                                                        
    }                                                                         
  } 

run函数的逻辑很明了,就是逐个执行prepare_handles队列的节点。我们回顾一开始的测试代码。因为他设置了libuv的运行模式是默认模式。又因为有或者的handle(prepare节点),所以他是不会退出的。他会一直执行回调。那如果我们要退出怎么办呢?或者说不要执行prepare队列的某个节点了。我们只需要stop一下就可以了。

代码语言:javascript
复制
 int uv_prepare_stop(uv_prepare_t* handle) {                               
    if (!uv__is_active(handle)) return 0;                                     

    // 把handle从prepare队列中移除,但是还挂载到handle_queue中
    QUEUE_REMOVE(&handle->queue);                                             

   // 清除active标记位并且减去loop中handle的active数
    uv__handle_stop(handle);                                                  
    return 0;                                                                 
  } 

stop函数和start函数是相反的作用,就不分析了。这就是nodejs中prepare阶段的过程。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档