【问题标题】:Vuex action cancel promiseVuex 动作取消承诺
【发布时间】:2021-02-02 02:09:20
【问题描述】:

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

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

我创建了一个从另一个 stackoverflow 答案提取的自定义 CancellablePromise 类,但它不适用于 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,而不是CancelablePromise。我的怀疑来自于查看the Vuex source,它再次似乎从操作返回的Promise 创建一个new Promise。我对 Typescript 的类型系统不是很熟悉,但除非我误读了这段代码,否则我认为我的 CancellablePromise 在这里“丢失”了。

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

【问题讨论】:

  • 我猜你可以将它添加到原型中:Promise.prototype.cancel = ...?
  • 正如您所指出的,Vuex 用新的(不可取消的)Promise 包装了您的 CancellablePromise,因此您无法直接从您的操作中访问您自己的承诺。除非您自己修补 Vuex,否则我认为没有办法取消承诺。
  • 除了调用cancel() 方法之外,您是否建议使用this.promise
  • @Roamer-1888 不,我不这么认为。为什么?

标签: javascript typescript vue.js promise vuex


【解决方案1】:

Extenting Promise 是混乱且不必要的。比较正常

  • 将 Promise 的 reject 方法公开给更广泛的世界(在 Promise 的构造函数之外),并在必要时调用它以使 Promise 采用其错误路径。
  • 与感兴趣的 Promise 竞争“取消 Promise”,但这里没有必要这样做,因为 setInterval 过程的承诺使 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'));
    }
}

【讨论】:

  • 这似乎不起作用,原因与我原来的解决方案不起作用的原因相同:dispatch 创建一个 new Promise 包装在操作中创建的 Promise。也许我做错了什么导致不起作用?
  • 哎哟!这让生活变得棘手。不知道有没有其他的派送方式;直接调用Someaction.foo() 的方式?
猜你喜欢
  • 2023-03-20
  • 1970-01-01
  • 2019-07-30
  • 2017-10-09
  • 2018-12-10
  • 1970-01-01
  • 2017-11-23
  • 2015-01-23
相关资源
最近更新 更多