当事件触发时触发 Promise

22

我整个项目都使用了(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();
});

我阅读了在Promise链中使用EventEmitter的答案。 这给了我一种执行“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"?


也许这会有所帮助:http://bahmutov.calepin.co/promisify-event-emitter.html - Matt Browne
缓存版本:https://web.archive.org/web/20160320100756/http://bahmutov.calepin.co/promisify-event-emitter.html - user1278519
2个回答

29

我假设您希望为每个事件执行不同的操作。即使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()
})

3
承诺只能被解决一次吗?我认为这对于多事件API并不适用。 - Chris Grimmett
@Grimtech 你说得对。如果你需要多次监听一个事件,它们不是正确的工具。 - JoshWillik
2
使用.once代替.on,这样如果一个事件被触发多次,它不会破坏你的承诺(当然,要小心使用)。 - Nepoxx
你可以在 Promise 中多次调用 resolve(),但是使用 .once 仍然是个好主意,因为等待 Promise 解决的任何内容只会得到它解决的第一个值。 - Lee Benson

1

我遇到了同样的问题,并编写了一个小型的 Promise 包装库(controlled-promise),可以将事件发射器 promisify 化。你的示例的解决方案如下:

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());

这种方法的好处:
  • 在 promise 保持 pending 状态时自动返回现有的 promise
  • 方便访问 resolve() / reject() 回调函数

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接