Задать вопрос
@quex

Есть ли лучше способ перейти от event-ов к callback-у в NodeJS?

есть некий сторонний модуль, наследуемый от EventEmitter, назовем его SuperModule. при вызове некоего метода #start, эмитируется событие error или started. передача колбэка в #start не предусмотрена разработчиком модуля.
необходимо выполнить цепочку вызовов асинхронных функций, одним из звеньев которой является вызов #start
Т.е. нужно обеспечить поддержку такого вызова (с передачей колбэка):
SuperModuleInstance.start(function(err){...});
Сейчас реализовано следующим образом:
var SuperModule = require('SuperModule');

SuperModule.prototype.startcb = function(callback){
	var cb = function(err){
		this.removeListener('error', cb);
		this.removeListener('started', cb);
		callback(err); // err == null при started
	}
	this.on('started', cb);
	this.on('error', cb);
	this.start();
}

...

async.series([
	...,
	SuperModuleInstance.startcb.bind(SuperModuleInstance),
	...], 
finalCallback);


есть ли более изящные альтернативы? просто уже не в первый раз возникает необходимость такое вытворять.
форк модуля, а также правки в node_modules не рассматриваются.
  • Вопрос задан
  • 2402 просмотра
Подписаться 2 Оценить Комментировать
Решения вопроса 1
k12th
@k12th
console.log(`You're pulling my leg, right?`);
Используйте адаптер. Типа такого:
var SuperModuleAdapter = function () {
    this.adapted = new SuperModule() 
}

SuperModuleAdapter.prototype = {
    start: function (callback) {
        var cb = function(err){
            this.adapted.removeListener('error', cb);
            this.adapted.removeListener('started', cb);
            callback(err); // err == null при started
        }
        this.adapted.on('started', cb);
        this.adapted.on('error', cb);
        this.adapted.start();
    }
}
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Вот ещё варианты:
var SuperModule = require('SuperModule');
var start_ = SuperModule.prototype.start;

SuperModule.prototype.start = function(cb){
    var fn = onceDelegate(cb);
    this
        .once('started', fn)
        .once('error', fn)
        ;
    start_.call(this);
};

function onceDelegate(fn) {
    return function(){
        var x_ = fn;
        fn = null;
        x_ && x_.apply(null, arguments);
    };
}


// ИЛИ, если сделать более общим:
function callbackInjector(Ctor, method, events) {
    var orig_ = Ctor.prototype[method];
    
    Ctor.prototype[method] = function(){
        var args = Array.prototype.slice.call(arguments),
            cb = args.pop(),
            listener = onceDelegate(cb),
            imax = events.length,
            i = -1
            ;
        while(++i < imax) this.once(events[i], listener);
        return orig_.apply(this, args);
    }
};

callbackInjector(SuperModule, 'start', ['started', 'error']);
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы