实现可中止API时如何优雅地管理AbortSignal事件监听器?

问题描述 投票:0回答:1

考虑这个简单的示例,可能是您编写了几次但现在可中止的函数:

/**
 * 
 * @param {number} delay 
 * @param {AbortSignal} [abortSignal]
 * @returns {Promise<void>}
 */
export default function timeoutPromise(delay, abortSignal) {
    return new Promise((resolve, reject) => {
        if(abortSignal) {
            abortSignal.throwIfAborted();
        }

        const timeout = setTimeout(() => {
            resolve();
        }, delay);

        abortSignal.addEventListener("abort", () => {
            clearTimeout(timeout);
            reject(new Error("Aborted"));
        });
    });
}

明显的问题是,如果超时正常成功,这不会清除 eventListener。可以做到,但是很难看:

/**
 * 
 * @param {number} delay 
 * @param {AbortSignal} [abortSignal]
 * @returns {Promise<void>}
 */
export default function timeoutPromise(delay, abortSignal) {
    return new Promise((resolve, reject) => {
        if(abortSignal && abortSignal.aborted) {
            reject(new Error("timeoutPromise aborted"));
        }
        let timeout = null;
        function abortHandler() {
            clearTimeout(timeout);
            reject(new Error("timeoutPromise aborted"))
        }
        timeout = setTimeout(() => {
            if(abortSignal) {
                abortSignal.removeEventListener("abort", abortHandler);
            }
            resolve();
        }, delay);

        if(abortSignal) {
            abortSignal.addEventListener("abort", abortHandler, {once: true});
        }
    });
}

对于这么简单的事情来说,有很多代码。我这样做对吗还是有更好的方法?

javascript promise abortcontroller
1个回答
0
投票

您可以对

AbortSignal
上的方法调用使用可选链接:

function delay(ms, signal) {
  return new Promise((resolve, reject) => {
    function done() {
      resolve();
      signal?.removeEventListener("abort", stop);
    }
    function stop() {
      reject(this.reason);
      clearTimeout(handle);
    }
    signal?.throwIfAborted();
    const handle = setTimeout(done, ms);
    signal?.addEventListener("abort", stop);
  });
}
© www.soinside.com 2019 - 2024. All rights reserved.