我需要一个独立的抽象来消除函数调用的抖动。虽然 deno/async 提供了这样的功能,但 AFAICT 无法取消已经运行的功能(例如,杀死子进程)。所以,前几天,我尝试自己实现这个需求:
function debounced<T>(
fn: (signal: AbortSignal) => Promise<T>,
ms: number,
): () => Promise<T> {
let controller: AbortController | null = null;
return async () => {
controller?.abort();
controller = new AbortController();
const signal = controller.signal;
await delay(ms, { signal });
return await fn(signal);
};
}
此测试应展示预期的行为:
Deno.test(async function debouncedAbortsRunningFunctions() {
// Arrange
using time = new FakeTime();
const enter = spy();
const exit = spy();
const fn = debounced(async (signal) => {
enter();
// simulate a long running function
await delay(100, { signal });
exit();
}, 100); // 100ms delay before calling the function
// Act
const p1 = fn();
time.tick(50);
const p2 = fn();
time.tick(150);
const p3 = fn();
time.tick(1000);
const actual = await Promise.allSettled([p1, p2, p3]);
// Assert
assertEquals(actual[0].status, "rejected");
assertEquals(actual[1].status, "rejected");
assertEquals(actual[2].status, "fulfilled");
assertSpyCalls(enter, 2); // 2 because the first call was aborted during the delay
assertSpyCalls(exit, 1); // 1 because the second call was aborted mid-execution
});
但是,我越来越
error: Promise resolution is still pending but the event loop has already resolved.
在 NodeJs 中运行等效代码会得到
(uncaught error) error: (in promise) AbortError: The signal has been aborted
。
我认为这表明未绑定/未等待的承诺以某种方式转义为“后台任务”,因此 AbortError 异常会引发事件循环。我只是不知道这会如何以及在哪里发生。
此时,我已经尝试了多种
debounced()
的实现,使用 new Promise(...)
等等。
我迷路了。如有任何帮助,我们将不胜感激!
你的测试确实在被
p1
ed 之前就拒绝了 await
(通过 Promise.allSettled
),这就是未处理的拒绝的来源。
我对 deno/测试不太熟悉,但我建议你尝试一下
// act
const p1 = fn();
const p2 = delay(50).then(fn);
const p3 = delay(200).then(fn);
const promise = Promise.allSettled([p1, p2, p3]);
time.tick(1200);
const actual = await promise;
任何拒绝都应该能够逃脱
Promise.allSettled
的处理(除非 Promise 构造和 allSettled
调用之间存在同步异常)。