前端封装一个Promise,可以在外部直接中止它

119 阅读2分钟

在 JavaScript 中,标准的 Promise 对象并没有提供直接中止的功能。然而,我们可以通过封装一个自定义的 CancelablePromise 来实现这一功能。以下是一个详细的代码示例和讲解,展示如何封装一个可以在外部直接中止的 Promise

代码示例

class CancelablePromise {
  constructor(executor) {
    this._hasCanceled = false;
    this._promise = new Promise((resolve, reject) => {
      executor(
        (value) => {
          if (!this._hasCanceled) {
            resolve(value);
          }
        },
        (reason) => {
          if (!this._hasCanceled) {
            reject(reason);
          }
        }
      );
    });
  }

  then(onFulfilled, onRejected) {
    return this._promise.then(onFulfilled, onRejected);
  }

  catch(onRejected) {
    return this._promise.catch(onRejected);
  }

  finally(onFinally) {
    return this._promise.finally(onFinally);
  }

  cancel() {
    this._hasCanceled = true;
  }
}
  1. 构造函数 (constructor):

    • this._hasCanceled:一个布尔值,表示 Promise 是否已被取消。
    • this._promise:一个新的 Promise 对象,接受一个 executor 函数。
    • executor 函数接收两个参数:resolvereject。我们对这两个参数进行了封装,使其在 Promise 被取消时不会被调用。
  2. then 方法:

    • 这个方法包装了内部的 Promisethen 方法,允许外部链式调用。
  3. catch 方法:

    • 这个方法包装了内部的 Promisecatch 方法,允许外部链式调用。
  4. finally 方法:

    • 这个方法包装了内部的 Promisefinally 方法,允许外部链式调用。
  5. cancel 方法:

    • 调用这个方法将 this._hasCanceled 设置为 true,表示 Promise 已被取消。

使用示例

function asyncTask() {
  return new CancelablePromise((resolve, reject) => {
    const timeoutId = setTimeout(() => {
      resolve('Task completed');
    }, 5000);

    return () => clearTimeout(timeoutId);
  });
}

const cancelablePromise = asyncTask();

cancelablePromise
  .then((value) => {
    console.log(value);
  })
  .catch((error) => {
    console.error(error);
  })
  .finally(() => {
    console.log('Promise settled');
  });

setTimeout(() => {
  cancelablePromise.cancel();
  console.log('Promise canceled');
}, 2000);

讲解示例

  1. 定义异步任务 (asyncTask):

    • 创建一个新的 CancelablePromise,在内部设置一个 setTimeout 模拟异步任务。
    • resolve 在 5 秒后被调用,表示任务完成。
  2. 创建可取消的 Promise (cancelablePromise):

    • 调用 asyncTask 返回一个 CancelablePromise 实例。
  3. 链式调用 thencatchfinally:

    • 使用标准的 Promise 方法处理异步任务的结果。
  4. 取消 Promise:

    • 使用 setTimeout 在 2 秒后调用 cancelablePromise.cancel() 取消 Promise
    • Promise 被取消时,resolvereject 都不会被调用。

通过这种方式,我们可以创建一个可取消的 Promise,并在需要时中止异步任务。这个封装在处理需要取消的异步操作时非常有用,例如网络请求或长时间运行的计算任务。