使用 extends 对内置的 Promise 进行子类化

Subclassing the Promise built-in using extends

我想将 cancel 方法添加到 Promise 内置的子类。为什么这不起作用?

class CancellablePromise extends Promise {
    constructor(executor) {
        let cancel = null
        super((resolve,reject) => {
            cancel = reject
            executor(resolve, reject)
        })
        this.cancel = cancel
    }
}

const p = new CancellablePromise((resolve) => setTimeout(resolve, 1000))
    .then(() => console.log('success'))
    .catch((err) => console.log('rejected', err))

p.cancel() // Uncaught exception

答案是Symbol.species吗?

问题是 thencatchfinally 创建并 return 一个 new 承诺,并且他们创建的新承诺 return 上面没有 cancel 方法。

要解决这个问题,您必须重写 then,以便它将 cancel 从当前实例复制到新实例:

class CancellablePromise extends Promise {
    constructor(executor) {
        let cancel = null;
        super((resolve,reject) => {
            cancel = reject;
            executor(resolve, reject);
        });
        this.cancel = cancel;
    }

    then(onFulfilled, onRejected) {
        const p = super.then(onFulfilled, onRejected);
        p.cancel = this.cancel;
        return p;
    }
}

const p = new CancellablePromise((resolve) => setTimeout(resolve, 1000))
    .then(() => console.log('success'))
    .catch((err) => console.log('rejected', err));

p.cancel();

您不需要执行 catchfinally,它们都是使用调用 then(根据规范)定义的。

我应该指出,关于可取消的承诺有很多细微差别,我没有详细讨论。可能值得深入阅读 Ben Lesh 的 Domenic Denicola's old (now withdrawn) cancellable promises proposal and this article

实际上,then方法returns是当前promiseclass的一个新实例,所以p.cancel方法会在返回的promise上定义,但是它指的是链中的最后一个承诺(catch 链)。 取消承诺比仅仅拒绝链中的第一个承诺要困难得多。至少,您需要拒绝链中最深的未决承诺并清理内部长期操作,如请求、流、setTimeout 等。 它们应该看起来像这样:

import CPromise from "c-promise2";

const delay= (ms, value)=>{
    return new CPromise((resolve, reject, {onCancel}) => {
        const timer = setTimeout(resolve, ms, value);    
        onCancel(() => {
            log(`clearTimeout`);
            clearTimeout(timer);
        })
    })
}

const p= delay(1000, 123).then(console.log);

p.cancel();