【问题标题】:How to have a function calling sequentially other functions and emitting events?如何让一个函数顺序调用其他函数并发出事件?
【发布时间】:2022-06-14 16:30:20
【问题描述】:

我不知道这是否重复,但我找不到答案。

我正在为 Web 应用程序开发一个 Typescript 库。 我需要一个函数f,它需要顺序调用其他函数并发出事件以供调用者监听。

伪代码:

function f(arg1, arg2, arg3) {
    const r1 = b()
    // wait for b execution
    emit('phase1', r1)
    const r2 = c()
    // wait for c execution
    emit('phase2', r2)
    const r3 = d()
    // wait for d execution
    emit('phase3', r3)
}
f('1', 3, '4').once('phase1', doSomething).once('phase3', somethingElse)

使用 async/await 将允许我按顺序执行内部函数,但在这种情况下,f 将需要返回一个 Promise,从而失去发出事件的可能性。 如果我选择返回一个事件发射器,我会失去可读性,因为无法按顺序等待内部函数。

我发现了这个有趣的库 Promievent,受 web3 的启发,但是,由于我认为这是一个不那么罕见的应用程序,我想知道是否有更好的方法/模式可以使用。

【问题讨论】:

  • doSomethingsomethingElse 到底是做什么的?你关心他们是否按那个顺序被调用吗?您是否需要像“事件侦听器”一样注册它们,您是否已确定 .once(…) 语法?
  • @alain ...关于到目前为止提供的答案/方法,还有什么问题吗?
  • 尽管答案很好,但由于需要调用迭代器,我发现生成器方法并不理想。我发现 Promievent 方法更适合我的需求。

标签: javascript function asynchronous generator eventemitter


【解决方案1】:

产生事件的Generator function 怎么样?例如

function* f() {
    const r1 = b()
    yield ['phase1', r1]
    const r2 = c()
    yield ['phase2', r2]
}

【讨论】:

  • 这需要我在每一步都调用next()。不是这样吗?
  • 生成实现迭代器协议(next())和可迭代协议,所以你可以使用for..ofloop。
【解决方案2】:

正如 Josh Bothun 已经建议的那样,可以选择一种基于 generator functionsgenerators 的方法。

从 OP 提供的 function f(arg1, arg2, arg3) { /* ... */ } 伪代码实现中可以看出,异步函数/方法可能作为此类函数的参数提供。而且由于需要await,因此每个函数调用的结果都必须采用async generator 方法,这使得OP 的原始f 也成为异步实现的函数。

至于 OP 的顾虑……

"...但在这种情况下,f 需要返回 [p]romise,从而失去发出事件的可能性"

...原因总是可以从任何异步函数发出/触发/调度事件。并且这样一个函数被包装/嵌入的承诺既不需要返回一个有意义的值,也不需要处理(例如await ...另见(未处理的)最终调用下面提供了异步实现)。

// an async function and event type generator-function.
async function* createAsyncFunctionAndEventTypeIterable(asyncFunctions) {
  let asyncFct;
  while (asyncFct = asyncFunctions.shift()) {

    yield (await asyncFct());
  }
}

// the OP's function which needs to be implemented as async.
async function awaitAndEmitOneAfterTheOther(...asyncFunctions) {
  // asyncFunctions = asyncFunctions.flat();

  const asyncIterable =
    createAsyncFunctionAndEventTypeIterable(asyncFunctions);

  for await (const [type, value] of asyncIterable) {
    emit(type, value);
  }
}


// test setup

// the event emitter mock.
function emit(type, value) {
  console.log('emit ...', { type, value });
}
function createAsyncTestFunctionAndEventType(delay, type, value) {
  return async function () {
    return await (
      new Promise(resolve => setTimeout(resolve, delay, [type, value]))
    );
  }
}
const [
  asyncFct_1,
  asyncFct_2,
  asyncFct_3,
] = [
  [2000, 'phase1', 'foo'], [1000, 'phase2', 'bar'], [3000, 'phase3', 'baz'],
].map(([delay, type, value]) =>
  createAsyncTestFunctionAndEventType(delay, type, value)
);

// execute test.
console.log('... test started ...');

awaitAndEmitOneAfterTheOther(asyncFct_1, asyncFct_2, asyncFct_3);
.as-console-wrapper { min-height: 100%!important; top: 0; }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 2021-04-22
    • 2019-04-22
    • 1970-01-01
    相关资源
    最近更新 更多