首页 > 解决方案 > Vuex 动作取消承诺

问题描述

我希望能够从我的 Vue 组件中取消已启动的承诺,特别是 Vuex 操作返回的承诺。

我的用例是我的 Vuex 操作轮询端点的状态,如果用户执行某个操作(示例中的关闭函数),我希望能够取消该轮询。

我创建了一个从另一个 stackoverflow 答案中提取的自定义 CancelablePromise 类,但它不适用于 Vuex。

可取消的承诺类(来自https://stackoverflow.com/a/60600274/2152511

export class CancellablePromise<T> extends Promise<T> {
  private onCancel: () => void;

  constructor(
    executor: (
      resolve: (value?: T | PromiseLike<T>) => void,
      reject: (reason?: any) => void,
      onCancel: (cancelHandler: () => void) => void
    ) => void
  ) {
    let onCancel: () => void;
    super((rs, rj) =>
      executor(rs, rj, (ch: () => void) => {
        onCancel = ch;
      })
    );
    this.onCancel = onCancel;
  }

  public cancel(): void {
    if (this.onCancel) {
      this.onCancel();
    }
  }
}

行动

async [SomeAction.foo]({ state, dispatch, commit, rootGetters }) {
    const cancellablePromise = new CancellablePromise<any>((resolve, reject, onCancel) => {
      const interval = setInterval(async () => {
        const status = await dispatch(SomeAction.bar);
        if (status === "goodstatus") {
          clearInterval(interval);
          resolve();
        } else if (status === "badstatus") {
          clearInterval(interval);
          reject();
        }
      }, 2000);

      onCancel(() => {
        clearInterval(interval);
        reject();
      });
    });

    return cancellablePromise;
  }

零件

data: (() => {
  promise: undefined as CancellablePromise<any> | undefined
}),

async call() {
  this.promise = this.$store
    .dispatch(SomeAction.foo)
    .then(response => {
      // do something
    }) as CancellablePromise<any>;
},

close(): void {
  if (this.promise) {
    this.promise.cancel(); // outputs cancel is not a function
  }
}

问题出现在不是close函数this.promise.cancel的函数中。

这在我看来是因为返回的对象dispatch确实是 Promise,而不是 CancellablePromise。我的怀疑来自查看Vuex 源代码,它似乎new Promise再次从操作返回的创建一个Promise。我对 Typescript 的类型系统不是很熟悉,但除非我误读了这段代码,否则我认为我的代码CancellablePromise在这里“迷路”了。

我怎样才能在这里完成我想做的事情?

标签: javascripttypescriptvue.jspromisevuex

解决方案


扩展 Promise 是混乱且不必要的。这更正常

  • 将 Promise 的reject方法暴露给更广泛的世界(在 Promise 的构造函数之外),并在必要时调用它以使 Promise 采用其错误路径。
  • 与感兴趣的 Promise 竞争“取消 Promise”,但这不是必需的,因为该setInterval过程的 Promise 使reject方法可用。

像这样的东西应该这样做(未经测试)。

行动

async [SomeAction.foo]({ state, dispatch, commit, rootGetters }) {
    let reject_, interval;
    const promise = new Promise((resolve, reject) => {
        reject_ = reject; // externalise the reject method
        interval = setInterval(async () => {
            const status = await dispatch(SomeAction.bar);
            if (status === 'goodstatus') {
                resolve();
            } else if (status === 'badstatus') {
                reject(new Error(status)); // for example
            } else {
                // ignore other states ???
            }
        }, 2000);
    });
    promise.cancel = reject_; // decorate promise with its own reject method.
    return promise.always(() => { clearInterval(interval) }); // clear the interval however the promise settles (resolve() or reject() above, or promise.cancel() externally).
}

零件

data: (() => {
    cancel: null
}),
async call() {
    this.close(new Error('new call was made before previous call completed')); // may be a good idea
    let promise = this.$store.dispatch(SomeAction.foo); // don't chain .then() yet otherwise the .cancel property is lost.
    this.cancel = promise.cancel; // store the means to force-reject the promise;
    return promise.then(response => { // now chain .then()
        // do something
    })
    .catch(error => {
        console.log(error);
        throw error;
    });
},
close(reason): void {
    if (this.cancel) {
        this.cancel(reason || new Error('cancelled'));
    }
}

推荐阅读