如何使用 clearTimeout 功能实现承诺的 setTimeout?

问题描述

下面的实现抛出一个错误(见下面的评论),如何解决这个问题?

interface PromiseWithAbort extends Promise<unkNown> {
  abort: () => void
}

export const pause = (
  ms?: number,cb?: (...args: unkNown[]) => unkNown,...args: unkNown[]
): PromiseWithAbort => {
  let timeout

  // Error: Property 'abort' is missing in type 'Promise<unkNown>'
  // but required in type 'PromiseWithAbort'.
  const promise: PromiseWithAbort = new Promise((resolve,reject) => {
    timeout = setTimeout(async () => {
      try {
        resolve(await cb?.(...args))
      } catch (error) {
        reject(error)
      }
    },ms)
  })

  promise.abort = () => clearTimeout(timeout)

  return promise
}

解决方法

问题是您分配给 promise 的承诺没有 abort 属性,但您分配的类型 promise 需要一个。解决此问题的一种简单方法是在将其分配给 promise 之前添加它。 (这也可以让您摆脱 promise 上的显式类型。)

还有其他一些事情,请参阅 *** 评论:

interface PromiseWithAbort extends Promise<unknown> {
    abort: () => void
}

export const pause = (
    ms?: number,cb?: (...args: unknown[]) => unknown,...args: unknown[]
): PromiseWithAbort => {
    let timeout: number; // *** Need the type in order to avoid implicit `any`
  
    // *** Add `abort` to the promise before assigning to `promise`
    const promise = Object.assign(
        new Promise((resolve,reject) => {
            timeout = setTimeout(async () => {
                try {
                    resolve(await cb?.(...args));
                } catch (error) {
                    reject(error);
                }
             },ms); // *** `ms` needs a default value,you're optionally passing `undefined`
        }),{
            abort: () => clearTimeout(timeout)
        }
    );
  
    return promise;
}

On the playground

也就是说,在 await 返回的 promise(如果有)上使用 cb 并将结果传递给 resolve 有点绕;相反,您可以将承诺传递给 resolve,这会将您创建的承诺解析为 cb(如果有)返回的承诺:

export const pause = (
    ms?: number,...args: unknown[]
): PromiseWithAbort => {
    let timeout: number;
  
    // *** Add `abort` to the promise before assigning to `promise`
    const promise = Object.assign(
        new Promise((resolve,reject) => {
            timeout = setTimeout(() => { // *** No need for `async`
                try {
                    resolve(cb?.(...args)); // *** No need for `await`,just resolve the promise to `cb`'s promise
                } catch (error) {
                    reject(error);
                }
             },ms);
        }),{
            abort: () => clearTimeout(timeout)
        }
    );
  
    return promise;
}

On the playground


就其价值而言,我不会在承诺中添加 abort,尤其是因为当您对该承诺使用 .then.catch 或在 {{ 1}} 函数,你从他们那里得到的承诺不会有 async 方法。相反,您可以考虑接受 AbortSignal

我也会删除 abort 并让 cb 成为一个纯粹的暂停函数。 pause 不必要地复杂化;您可以使用 cb.then,然后直接在代码中调用 await

这是一个例子:

cb

On the playground