神之门V8-----Event loop的舞池盛宴(1)

这里写图片描述

故事的开始

由于各种原因,没有继续写V8引擎系列文章有相当长的一段时间了,今天本来不打算写解剖node的文章的,但一个contextify被扰乱的native问题实在引起了我对它的兴趣,好吧,我又重新回归了这个神奇的世界,看了一些文档资料,只能有个大概的印象,然而作为一个独操引擎的男人,既然入了坑,怎么能浅尝则止呢,结合源代码,我仔细地观察了eventloop,领教了这个神奇的存在给node注入了强大的生命力,所以一个node的eventloop到底干了什么事情呢,将在本文一一为大家揭晓。

这里写图片描述

首先,我们先来看看一个同样带有Event字样的“类“eventloop。

EventEmitter & Eventloop

很多人搞不清EventEmitter跟loop的关系,甚至还有混为一谈者,不过在应用层的nodeAPI确实容易让人造成误解,毕竟很多异步事件的监听,处理都是通过emitter来完成的,像这样

//socket inherited from EventEmitter
socket.on('myEvent', () => {
    //handler
});

这种代码写起来很有异步风格,其实如果我们开发一个网络IO的程序确实也会感觉这很异步,其实,EventEmitter的API是完全同步的,它的模型也十分简单, OK,我来简单模拟一下emit里面发生的事情。

EventEmitter.prototype.emit = function(event_type) {
    //main code
    //handle some TypeErrors
    //get all events registered in Emitter
    events = this._events;
    //type used for the key to find which event handler should be invoked
    handle = events[event_type];
    var isFn = typeof handler === 'function';
    len = arguments.length;
    switch (len) {
    // fast cases
        case 1:
            //handle.call(self) in emitNone
            emitNone(handler, isFn, this);
            break;
        case 2:
            emitOne(handler, isFn, this, arguments[1]);
            break;
        case 3:
            emitTwo(handler, isFn, this, arguments[1], arguments[2]);
            break;
        case 4:
            emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
            break;
        // slower back to normal state without Inline cache optimization
        default:
            emitMany(handler, isFn, this, args);
  }
}

现在应该可以很清楚的看到emit并没有任何的异步迹象,其实从node的event模块并没有依赖任何的native模块就可以猜出来这个API就是纯同步的,我再来写一段更能揭露它真面目的代码。

const util = require('util');
const EventEmitter = require('events');

function MyEmitter() {
    //initialize the EventEmitter for the listeners queue
    EventEmitter.call(this);
    //do something first
    // this.emit('event_x'); handler can not receive this event because it emitted synchronously when MyEmitter instantiated
    //it works
    setImmediate(emitInCallback, this, new Error('temporary error instance to preserve lost call stack'));
}

function emitInCallback(self, err) {
    if(err) {
        self.emit('error_event');
        return;
    }
    self.emit('event_x');
}

util.inherits(MyEmitter, EventEmitter);

let em = new MyEmitter();

em.on('event_x', () => {
    //but the emitter API is synchronously
    logger.log('I can receive this asynchronously event ');
});

em.on('error_event', () => {
    logger.log('I can receive this error event ');
});

可以看到,如果我们直接在constructor上调用emit,很遗憾并不能接受到事件,那是因为事件在实例化的那一瞬间已经触发,由于不是异步代码,所以只有在触发的那一瞬间立即调用了handler,这个机会一旦错过便并不再有,但我就是要任性地把事件的触发放在constructor上,好的,我需要把它手动变成异步代码,只需要添加到事件循环最后一帧(phase)的check event,即调用setImmediate函数,然后稍等一会,没错,在运行这段代码并不是马上给我们呈现log,我们需要等待一段微不足道执行event loop前面一些帧的额外CPU时间,虽然在v8运行我们的代码时,回调的触发函数,我以后的文章会叫做这是一个Stub,即js函数在引擎层的表示,好的,这个Stub现在执行期的入口被注册,然后等到这一轮loop快要结束时执行Stub缓存的代码,关于这个函数的更多细节我会结合源代码给大家分享。

不过,这个异步调用需要注意一个异常处理的问题,原因很简单,这种调用会导致调用栈帧的缺失,看着debugger上的调用栈根本对错误手足无措,上面的代码在调用时临时实例化了一个Error对象,手动保护栈帧缺失。

这里写图片描述

OK,这里我们分清楚了同步的API跟依赖于native层eventloop的异步的代码行为,这就是个存在于native层的过程,所以我们要研究它,必须深入native层进行分析,下一篇将带领大家结合源代码进行解剖~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值