我不知道以前是否有人遇到过这个问题。
在我有一个EventHandler之前,它看起来是这样的:
export interface EventHandler {
name: string;
canHandleEvent(event: EventEntity): boolean;
handleEvent(event: EventEntity): Promise<void>;
}
我的过滤函数将正常工作,我的测试也通过了-我使用
过滤事件:messages.forEach(message => {
const event: EventEntity = JSON.parse(message.Body);
this.handlers
.filter(handler => handler.canHandleEvent(event)) // WORKED WELL
.forEach(handler => {
// LOGIC
});
目前,我们必须将canHandleEvent
更改为Boolean或Promise。因为我们有一些承诺要解决,并确定事件是否可以处理。
export interface EventHandler {
// ...
canHandleEvent(event: EventEntity): boolean | Promise<boolean>;
}
因此,为了求解它,我使用了Promise.resolve
和Promise.all
。没有运气:
messages.forEach(async message => {
const event: EventEntity = JSON.parse(message.Body);
const handlersResolved = await Promise.all(this.handlers);
handlersResolved
.filter(handler => handler.canHandleEvent(event))
.forEach(handler => {
现在,我的测试通过了承诺canHandleEvent
,但它们失败了通过的事件,即boolean
。它们是这样的:
class HandlerB implements EventHandler {
name = HandlerB.name;
numRuns = 0;
canHandleEvent(event: EventEntity): boolean {
console.log('event', event)
return event.eventType === EventType.ONE_EVENT || event.eventType === EventType.SECOND_EVENT;
}
async handleEvent(event: EventEntity): Promise<void> {
return new Promise(resolve => {
setTimeout(() => {
this.numRuns += 1;
resolve();
}, 25);
});
}
}
我的测试现在失败了,之前是通过的是:
it('Should process handlers that match, including canHandleEvent that returns Promise<boolean> TRUE', async () => {
setHandlers([handlerA, handlerB, handlerC]);
const event = await createEvent(EventType.SECOND_EVENT);
await sleep(1000);
expect(handlerA.numRuns, 'handleA').to.eql(0);
expect(handlerB.numRuns, 'handleB').to.eql(1);
expect(handlerC.numRuns, 'handleC').to.eql(1); // handlerC is Promise<boolean>, it works fine
expect(errorHandler.numRuns).to.eql(0);
handlerC.numRuns = 0;
});
it('Should allow handlers to pass even if one has an error', async () => {
setHandlers([handlerA, handlerB, errorHandler]);
const event = await createEvent(EventType.USER_REGISTRATION_STATUS);
await sleep(1000);
expect(handlerA.numRuns, 'handlerA').to.eql(1);
expect(handlerB.numRuns, 'handlerB').to.eql(1);
expect(errorHandler.numRuns, 'errorHandler').to.eql(1);
});
对于如何解决这个问题有什么想法吗?我试图确定是否承诺或布尔在.filter
内部之前,但仍然没有运气:
this.handlers
.filter(async handler => {
if(typeof handler.canHandleEvent(event).then == 'function') {
const result = await Promise.resolve(handler.canHandleEvent(event))
console.log('IS PROMISE!!', result);
return result
}
console.log('IT IS NOT PROMISE', handler.canHandleEvent(event))
return handler.canHandleEvent(event)
})
目前,我们必须将
canHandleEvent
更改为Boolean或Promise…
要清楚,这是一个巨大的语义变化,将波及使用该方法的每一层代码。例如,你不能再直接使用filter
了,任何使用它的同步函数现在都可能是异步的(并且从根本上说,"可能是异步的")。="asynchronous"。但如果一定要发生,那就一定要发生!: -)
您使用canHandleEvent
的原始代码如下:
messages.forEach(message => {
const event: EventEntity = JSON.parse(message.Body);
this.handlers
.filter(handler => handler.canHandleEvent(event)) // WORKED WELL
.forEach(handler => {
// LOGIC
});
});
必须变成异步的,像这样:
// Handles things in parallel, not series
await/*or return*/ Promise.all(messages.map(message => {
const event: EventEntity = JSON.parse(message.Body);
return Promise.all(this.handlers.map(handler => async {
if (await handler.canHandleEvent(event)) {
// LOGIC
}
}));
}));
注意每一层是如何受到影响的。messages.forEach
变成了通过messages.map
构建一个承诺数组,并通过await
等待它们(或者使用.then
等,或者返回到调用函数)。对于每条消息,我们对处理程序做同样的事情,因为我们无法知道处理程序是否可以同步处理某些内容。(不需要Promise.resolve
,Promise.all
将为您处理。)
上面的代码假设所有这些都可以重叠(消息和消息的处理程序),而之前因为它们都是同步的,所以它们都是串联发生的(一个消息的所有相关处理程序,按顺序,然后是下一个消息的所有处理程序,等等)。如果你需要像这样串联,你可以使用for-of
loops:
// Handles things in series, not parallel
// (In an `async` function)
for (const message of messages) {
const event: EventEntity = JSON.parse(message.Body);
for (const handler of this.handlers) {
if (await handler.canHandleEvent(event)) {
// LOGIC
}
}
}
在这两种情况下,可以处理返回boolean
的(同步)和返回promise的不同,但这会使代码复杂化。
要解决您的问题,我认为最简单的方法是首先用期望值填充数组,以便您可以正确过滤。
const transformedHandlers = await Promise.all(this.handlers.map(async handler => {
return {
...handler,
eventCanBeHandled: await handler.canHandleEvent(event)
}
}))
这将转换数组,使您有一个键,显示可以处理的处理程序。
要完成它,你可以像往常一样使用你的代码,但不是检查
canHandleEvent
使用const transformmedhandlers中引入的新字段
下面的是例子:
transformedHandlers
.filter(handler => handler.eventCanBeHandled)
.forEach(handler => {
// LOGIC
});
这应该足以让你的代码像以前一样工作。
对不起,我的英语不好。这不是我的母语