在异步功能中不相关的等待期间拒绝响应事件

时间:2018-08-15 14:36:01

标签: javascript asynchronous promise

当事件在函数内触发时,拒绝异步函数的“首选”方式是什么?

我目前通过用Promise.race包装每个异步操作来做到这一点:

async function exampleFunction(emitter){
    let lastError;
    emitter.once('error',error=>lastError=error);
    function createCancellable(promise){
        //cancel if any error occurred in between
        if(lastError)throw new Error(lastError);

        //race between the promise, and the next error occurring
        let _callback;
        return Promise.race([
            promise,
            new Promise((resolve, reject) =>{
                emitter.on('error', _callback = reject);
            }),
        ]).finally(() => {
            //cleanup
            if (_callback)
                emitter.removeListener('error', _callback);
        });
    }

    //actual code
    await createCancellable(someAsyncFunction());
    await createCancellable(anotherAsyncFunction());
} 

是否有更好/更简洁的方法?

1 个答案:

答案 0 :(得分:3)

我认为没有其他方法可以执行您要说的话,因为从根本上说,您在每个阶段都需要在两个诺言之间进行竞赛(事件发射器的错误以及您所做的确实在做)。

但是它可能要简单得多,这可能会使它更美味:

async function exampleFunction(emitter){
    const pError = new Promise((_, reject) => {
        emitter.once('error', reject);
    });
    const createCancellable = promise => Promise.race([pError, promise]);

    await createCancellable(someAsyncFunction());
    await createCancellable(anotherAsyncFunction());
} 

将所有逻辑(对someAsyncFunctionanotherAsyncFunction的调用)包装到单个async函数中,然后与错误竞争,这是很诱人的,但是没有什么能阻止新的错误发生时,包装器从继续执行到完成。因此,您对单个种族的处理方式很有意义。


发表评论:

  

这捕获了同步代码正确运行时排队的任何错误吗?

从下一个await开始,它可能还不及您。让我们添加一些同步代码:

async function exampleFunction(emitter){
    // Start synchronous block 0
    const pError = new Promise((_, reject) => {
        emitter.once('error', reject);
    });
    const createCancellable = promise => Promise.race([pError, promise]);

    // End synchronous block 0
    await createCancellable(someAsyncFunction());
    // Start synchronous block 1
    doThis();            // All of these are synchronous, starting
    doThat();            // after `someAsyncFunction`'s promise resolves
    doSomethingElse();   // and before we call `anotherAsyncFunction`
    // End synchronous block 1
    await createCancellable(anotherAsyncFunction());
    // Start synchronous block 2
    doYetAnotherThing(); // This is synchronous again, starting after
    keepDoingStuff();    // `anotherAsyncFunction`'s promise resolves and
    okayLastThingNow();  // continuing until the end of the function
    // End synchronous block 2
}

两种情况:

  1. 在{em} someAyncFunction的处理过程中,它正在等待其他完成的事件时,发生了eventemitter的错误。调用事件发射器的事件回调的作业将运行,拒绝pError。这立即拒绝了Promise.race并拒绝了exampelFunction承诺; await createCancellable(someAsyncFunction());行之后没有任何代码。

  2. {em> someAsyncFunction的承诺在运行“同步块1”时,发生了emem事件。在这种情况下,同步块将继续,因为无法中断同步作业以执行其他操作,因此来自事件发射器的回调被放入作业队列中。然后,当您执行await时,当前作业结束并且可以运行其他作业。如果运行事件发射器的事件回调,它将拒绝该承诺,因此exampleFunction从下一个await开始被拒绝-但是anotherAsyncFunction 可以被调用并开始,因为发生在调用Promise.race之前。如果要避免触发anotherAsyncFunction,则需要插入另一个await,请参见***注释:

    async function exampleFunction(emitter){
        // Start synchronous block 0
        const pError = new Promise((_, reject) => {
            emitter.once('error', reject);
        });
        const createCancellable = promise => Promise.race([pError, promise]);
    
        // End synchronous block 0
        await createCancellable(someAsyncFunction());
        // Start synchronous block 1
        doThis();            // All of these are synchronous, starting
        doThat();            // after `someAsyncFunction`'s promise resolves
        doSomethingElse();   // and before we call `anotherAsyncFunction`
        // End synchronous block 1
        await createCancellable(Promise.resolve()); // ***
        await createCancellable(anotherAsyncFunction());
        // Start synchronous block 2
        doYetAnotherThing(); // This is synchronous again, starting after
        keepDoingStuff();    // `anotherAsyncFunction`'s promise resolves and
        okayLastThingNow();  // continuing until the end of the function
        // End synchronous block 2
    }
    

要更简单地处理#2,您可以传递createCancellable回调以进行调用,如下所示:

async function exampleFunction(emitter){
    // Start synchronous block 0
    const pError = new Promise((_, reject) => {
        emitter.once('error', reject);
    });
    const createCancellable = (callback, ...args) => {
        return Promise.race([pError, Promise.resolve()])
            .then(() => Promise.race([pError, callback(...args)]));
    };

    // End synchronous block 0
    await createCancellable(someAsyncFunction); // *** no ()
    // Start synchronous block 1
    doThis();            // All of these are synchronous, starting
    doThat();            // after `someAsyncFunction`'s promise resolves
    doSomethingElse();   // and before we call `anotherAsyncFunction`
    // End synchronous block 1
    await createCancellable(anotherAsyncFunction); // *** no ()
    // Start synchronous block 2
    doYetAnotherThing(); // This is synchronous again, starting after
    keepDoingStuff();    // `anotherAsyncFunction`'s promise resolves and
    okayLastThingNow();  // continuing until the end of the function
    // End synchronous block 2
}

请注意,我们不会调用 someAsyncFunctionanotherAsyncFunction,我们只是将函数传递给createCancellable,并在首先检查事件发射器的拒绝后让它们调用

因此,这可能不只是简单地抛出await createCancellable(Promise.resolve());就简单了。 :-)