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

时间:2017-09-21 15:05:46

标签: javascript callback async-await cancellation

在async / await之前,当我的代码使用了回调时,我能够做三件事:(1)用结果调用回调,(2)用Error调用回调,或者(3)不调用回调一点都不。

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

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

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

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

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

这相当于没有调用回调。

但我不知道这是否只是一遍又一遍地泄漏记忆。

如果没有我上面提到的缺点,是否有更好的等效物?

1 个答案:

答案 0 :(得分:1)

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

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

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

根据这些答案,这将被垃圾收集,因为永远不会使用返回的promise,并且在promise函数参数内没有与堆的连接。

Do never resolved promises cause memory leak?

Are JavaScript forever-pending promises bad?

但是,这很可能不是您想要做的,因为上游呼叫者可能想知道他们的操作已被取消。如果操作是由用户通过UI启动的,则是,取消而不告诉调用者可能是好的,但是如果操作是以编程方式启动的并且以某种其他方式取消,例如,由用户,然后调用代码可能需要知道,以便它可以再次尝试,或清理资源。

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

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