简体   繁体   中英

What is the equivalent of not calling a callback when inside an async function?

Before async/await, when my code used callbacks, I was able to do three things: (1) call the callback with a result, (2) call the callback with an Error, or (3) not call the callback at all.

Case (3) was used in situations like this: say that you have a zoom button and a user can click it to render an image at a higher resolution, and this is an async process. If the user clicks the zoom button again, then the first render is no longer relevant, and can be canceled to let the new zoom level render run. I handled this by returning from inside the function without calling the callback, eg

if (process.wasCanceled()) {
  return;
}
// ...
callback(someResult);

With async/await, there are only two things that you can do: return or throw. Currently, I've been using throw to indicate that the operation was canceled, since returning can falsely indicate that upstream processes should keep running. But the problem with throwing is that all the upstream callers need to know that it's not really an error, per se, and so they may need to check the type of the error.

Another crazy idea I had was to create a promise that never returns. Eg await never() , where the function is defined like this:

async function never () {
    return new Promise(function () {});
}

That is sort of the equivalent of not calling a callback.

But I don't know if that would just leak memory over and over.

Is there a better equivalent without the drawbacks I mentioned above?

If absolutely necessary, you can await a promise that never returns. This is the equivalent of not calling a callback.

async function never () {
    return new Promise(function () {});
}

async function op (process) {
    // ...
    if (process.wasCanceled()) await never();
    // ...
}

According to these answers, this will be garbage collected, because the returned promise is never used and there are no connections to the heap inside the promise's function argument.

Do never resolved promises cause memory leak?

Are JavaScript forever-pending promises bad?

However, this is most likely not what you want to do, since upstream callers may like to know that their operation has been canceled. If the operation was initiated by a user through the UI, then yes, canceling without telling the caller is probably OK, but if the operation was initiated programmatically and cancelled some other way, eg by the user, then the calling code might need to know that, so that it can try again, or clean up resources.

For this reason, the solution is to throw an error, of a specific class so that the caller can detect that the process was cancelled. Eg

class ProcessCanceledError extends Error {
    ...
}

async function render (process) {
    while (...) {
        // do some rendering
        await delay(20);
        if (process.wasCanceled()) throw new ProcessCanceledError();
    }
}

var zoomProcess;

async function zoom () {
    let process = new Process();
    if (zoomProcess != null && !zoomProcess.isDone()) {
        zoomProcess.cancel();
    }
    try {
        await render();
    } catch (e) {
        // or you could do e.process === process
        if (e instanceof ProcessCanceledError &&
            process.wasCanceled() // make sure it was actually ours
        ) {
            // this assumes we are a top level function
            // otherwise, you would want to propagate the error to caller's caller
            return;
        }
        throw e;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM