为什么说promise不能取消是一个缺点

什么场景下我需要取消一个promise?
我不理解的地方:
http是无状态的,那取消是不是意味着新的请求?
还是说取消仅是本地取消,不处理之前请求的结果?

阅读 14.3k
7 个回答

Promise 的设计就是一个状态机,pending 到 resolve / reject 的状态变换是单向且唯一的,没有所谓的 cancel 状态。cancel 的加入会带来更多的状态问题,并不适合 Promise 这一模式来处理(这类场景下,RxJS 这类 FRP 的方案应该更为适合)。

cancel 会带来什么状态问题呢?拿电商的退款来举例子。你买了一个东西(开始一个 pending 的 promise),然后东西还没收到(还没 resolve),你后悔了,点击了退款(把状态改为 cancel),但这时退款流程也不能立刻生效,需要审核(cancel 后不能立刻 reject),那这时候你发工资了,又不想退款了,又点了【取消退款】,这时候又是一个异步的状态更改(把 cancel 再 cancel 掉),而【取消退款】也是异步的,你还能取消【取消退款】的操作(把 cancel 在 cancel 掉前再 cancel 掉?)别忘了,这时候每一步状态变化还都可以对应到 resolve 和 reject 呢。好的同学们,接下来请画出流程的状态变化图,并编码实现这个支持 cancel 的 promise?

这是一道送命题啊。

@ewind @lzszone cancel promise 是有的。。。不过跟很多人理解的 cancel 是不同的。。。已经发生的操作是不能 cancel 的,要 cancel 也只是 cancel 这个操作本该有的后续操作。。。

例如我本来要 fetch(url) 得到响应 resp 后,根据 resp 的内容修改一个变量。。。
代码是:fetch(url).then(resp => global.something = resp.xxx);

然后我要把这个操作 cancel 掉,应该:

var p = make_cancelable(fetch(url));
p.then(resp => global.something = resp.xxx);
do_something();
p.cancel();

这里,请求发送是免不了的。。。但是得到响应后,我们可以忽略这个响应,让它不修改那个变量。。。这就是promise 的 cancel。。。

最简单,但有一些副作用的 make_cancelable 实现是:

function cancelable(promise) {
    let cancel = false;
    let p = promise.then(val => {
        if (cancel) {
            throw cancelable.cancel_error;
        }
        return val;
    });
    p.cancel = () => cancel = true;
    return p;
}
cancelable.cancel_error = new Error('promise canceled.');

另外有个基本完美的 cancelable_promise 实现。。。
https://github.com/hjylewis/t...

具体的关于 cancelable_promise 的讨论可以看
https://github.com/facebook/r...
这是 react 的 this.isMounted api 的讨论,里面有使用 cancelable_promise 来解决去掉该 api 后会引起的问题。。。

比如页面需要1分钟刷新一次,但是接口特别慢,所以导致我下次发送请求的时候,上次的请求还没有回来,在这种情况下我就想取消上次的请求。

同时进行两个操作(两个Promise),其中一个完成了就把另一个取消掉,最后返回先成功的结果。

但是取消都是坑,例如多线程做取消意味着异步异常,就是操作到一半突然被打断, 很难进行恢复或者回滚。只有纯CPU的独立的计算任务才能安全地取消。

至于取消然后取消“取消”是不会有的,规矩进入cancel状态(半死不活状态)就不能切换到其他状态就行了。

比如点赞,取消点赞操作
如果是使用promise当你误点赞了你想点取消赞时, 你就必须等点赞操作结束才能操作
所以有了加强版的promise → observable

promise不应该被cancel,否则应该用rxjs来描述行为。
否则各种奇怪的边界情况根本无法处理。

推荐问题
宣传栏