No sé si esto está duplicado, pero no pude encontrar una respuesta.
Estoy desarrollando una biblioteca Typescript para una aplicación web. Necesito una función f que necesitaría llamar secuencialmente a otras funciones y emitir eventos para que la persona que llama los escuche.
Pseudocódigo:
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) Usar async/await me permitiría ejecutar secuencialmente las funciones internas, pero en ese caso f necesitaría devolver una Promesa, perdiendo así la posibilidad de emitir eventos. Si elijo devolver un emisor de eventos, perdería legibilidad ya que las funciones internas no podrían esperarse secuencialmente.
Encontré esta interesante biblioteca Promievent , inspirada en web3, pero, dado que creo que esta es una aplicación no tan rara, me preguntaba si hay un mejor enfoque/patrón para usar.
¿Qué tal una función de generador que produce eventos? p.ej
function* f() { const r1 = b() yield ['phase1', r1] const r2 = c() yield ['phase2', r2] }Como ya sugirió Josh Bothun , se podría elegir un enfoque basado en generator functions generadoras y generators .
Dado que, por lo que se puede ver en la implementación de pseudocódigo proporcionada por el OP de la function f(arg1, arg2, arg3) { /* ... */ } , las funciones/métodos asíncronos podrían proporcionarse como parámetros para dicha función. Y dado que uno debe await el resultado de cada llamada de función, debe optar por un enfoque de generador asíncrono que haga que el f original del OP también sea asíncrono para ser implementado.
Y en cuanto a las preocupaciones del OP...
"... pero en ese caso
fnecesitaría devolver una [p]romita, perdiendo así la posibilidad de emitir eventos"
... de causa uno siempre puede emitir/disparar/despachar eventos desde cualquier función asíncrona. Y la promesa en la que dicha función está envuelta/incrustada no siempre necesita devolver un valor significativo ni siempre necesita ser manejada (aquí, por ejemplo, por await ... vea también la invocación final (no manejada) de lo que se proporciona a continuación implementación asíncrona).
// 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; }