在异步函数中不调用回调相当于什么?

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

在 async/await 之前,当我的代码使用回调时,我可以做三件事:(1) 调用带有结果的回调,(2) 调用带有错误的回调,或 (3)根本不调用回调。

案例(3)用于这样的情况:假设您有一个缩放按钮,用户可以单击它以更高分辨率渲染图像,这是一个异步过程。如果用户再次点击缩放按钮,则第一次渲染不再相关,可以取消让新的缩放级别渲染 运行。我通过从函数内部 returning 处理这个而不调用回调,例如

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

使用 async/await,您只能做两件事:return 或抛出。目前,我一直在使用 throw 来指示操作被取消,因为 returning 可以错误地指示上游进程应该保持 运行ning。但是抛出的问题是所有上游调用者都需要知道它本身并不是真正的错误,因此他们可能需要检查错误的类型。

我的另一个疯狂想法是创建一个永不 return 的承诺。例如。 await never(),其中函数定义如下:

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

这相当于不调用回调。

但我不知道那样会不会一遍又一遍地泄漏内存。

有没有没有我上面提到的缺点的更好的等价物?

如果绝对必要,您可以等待永远不会 returns 的承诺。这相当于不调用回调。

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

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

根据这些答案,这将被垃圾收集,因为返回的承诺从未使用过,并且在承诺的函数参数内没有与堆的连接。

Do never resolved promises cause memory leak?

但是,这很可能不是您想要做的,因为上游调用者可能想知道他们的操作已被取消。如果操作是由用户通过 UI 发起的,那么是的,在不告诉调用者的情况下取消可能是可以的,但如果操作是以编程方式发起的并以其他方式取消,例如由用户调用,那么调用代码可能需要知道这一点,以便它可以重试或清理资源。

出于这个原因,解决方案是抛出特定 class 的错误,以便调用者可以检测到进程已被取消。例如

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;
    }
}