Trigger Promise, когда происходит пожар

Весь мой проект использует (Bluebird) Promises, но есть одна конкретная библиотека, использующая EventEmitter.

Я хочу добиться чего-то вроде:

Promise.on('connect', function() {
    x.doSomething();
}).then(function() {
    return new Promise(function(resolve) {
        y.doAction(resolve); // this will result in `eventB` getting emitted
    });
}).on('eventB', function() {
    z.handleEventB();
}).then(function() {
    z.doSomethingElse();
});

Я прочитал ответ EventEmitter в середине цепочки Promises. Это дает мне способ выполнить обратный вызов для события "connect". Здесь, где я до сих пор

var p = new Promise(function(resolve) {
    emitter.on('connect', resolve);
});
p.on = function() {
    emitter.on.apply(emitter, arguments);
    return p;
};
p.on('connect', function() {
    x.doSomething();
}).then(function() {
    return new Promise(function(resolve) {
        y.doAction(resolve); // this will result in eventB getting emitted
    });
});

Теперь, как цепляться дальше для 'eventB'?

Ответ 1

Я предполагаю, что вы хотите сделать другую цепочку вещей для каждого события. Даже если eventB запускается действиями connect, вы можете рассматривать его как другой поток логики.

Обратите внимание: во избежание путаницы для вас и для всех, кто должен читать эту кодовую базу, я бы рекомендовал не дополнить обещания дополнительными методами, если вы не очень тщательно документируете их.

Из вашего примера кажется, что следующее будет работать.

var Promise = require( 'bluebird' )
var emitter = someEmitter()
var connected = new Promise( function( resolve ){
    emitter.on( 'connect', resolve )
})

var eventBHappened = new Promise( function( resolve ){
    emitter.on( 'eventB', resolve )
})

connected.then( function(){
    return x.doSomething()
}).then( function(){
    return y.doSomethingElse() // will trigger 'eventB' eventually
})

// this promise stream will begin once 'eventB' has been triggered
eventBHappened.then( function(){ 
    return z.doSomething()
})

Если вы хотите упростить эту константу

var p = new Promise( function( resolve ){
    emitter.on( 'something', resolve )
})

Вы можете использовать что-то вроде этого

function waitForEvent( emitter, eventType ){
    return new Promise( function( resolve ){
        emitter.on( eventType, resolve )
    })
}

Который превращает решение кода выше в

var Promise = require( 'bluebird' )
var emitter = someEmitter()

function waitForEvent( eventEmitter, eventType ){
    return new Promise( function( resolve ){
        eventEmitter.on( eventType, resolve )
    })
}

waitForEvent( emitter, 'connect' ).then( function(){
    return x.doSomething()
}).then( function(){
    return y.doSomethingElse() // will trigger 'eventB' eventually
})

// this promise stream will begin once 'eventB' has been triggered
waitForEvent( emitter, 'eventB' ).then( function(){ 
    return z.doSomething()
})

И поскольку функции в Javascript фиксируют область, в которой они были определены, этот код может быть дополнительно упрощен до

var Promise = require( 'bluebird' )
var emitter = someEmitter()

function waitForEvent( type ){
    return new Promise( function( resolve ){
        //emitter has been captured from line #2
        emitter.on( type, resolve ) 
    })
}

waitForEvent( 'connect' ).then( function(){
    return x.doSomething()
}).then( function(){
    return y.doSomethingElse() // will trigger 'eventB' eventually
})

// this promise stream will begin once 'eventB' has been triggered
waitForEvent( 'eventB' ).then( function(){ 
    return z.doSomething()
})

Ответ 2

Я столкнулся с одной и той же проблемой и написал небольшую библиотеку обеими обещаниями (controlled-promise), которая позволяет обещать излучатели событий. Решение для вашего примера:

const Promise = require('bluebird');
const ControlledPromise = require('controlled-promise');

const emitter = someEmitter();
const waiting = new ControlledPromise();

function waitForEvent(type) {
    return waiting.call(() => {
       emitter.once(type, event => waiting.resolve(event));
    });
}

waitForEvent('connect')
    .then(() => x.doSomething())
    .then(() => waitForEvent('eventB'))
    .then(() => z.doSomethingElse());

Преимущества такого подхода:

  • автоматическое возвращение существующих обещаний во время ожидания
  • доступ к resolve()/reject() обратным вызовам