扩展JavaScript承诺



注意:

这个问题已经作为重复问题关闭,尽管我看不出它与引用的问题是如何相同的。

在这里,我要问的是关于延长Promise课程的问题。这个问题是不同的,尽管它确实提到了访问executor函数的共同目标。


我正在尝试使用ES6class语法扩展JavaScript Promises。在扩展类中,我希望使resolvereject函数更易于访问。

这是一个测试脚本:

var executor = {};
var promise = new Promise((resolve,reject) => {
executor = {resolve,reject};
});
promise.resolve = message => { executor.resolve(message ?? true); };
promise.reject = message => { executor.reject(message ?? false); };
document.querySelector('button#ok').onclick = event => promise.resolve('OK');
document.querySelector('button#cancel').onclick = event => promise.reject('Cancelled');
promise
.then(result => {console.log(result); })
.catch(error => {console.log(error); });
<button id="ok" type="button">OK</button>
<button id="cancel" type="button">Cancel</button>

最终,代码将成为伪对话框的一部分。

目前,Promise构造函数将resolvereject函数存储在一个外部变量中,另外两个方法被附加到生成的promise对象上。

我认为在一个继承的对象中这样做应该是一项简单的任务:

class Promise2 extends Promise {
constructor() {
//  add the resolve and resolve functions as instance methods
}
}

问题是构造函数需要调用super()来实例化this,我看不出如何从那里继续。

有没有可能以这种方式扩展Promise,或者有没有其他方式将对resolvereject函数的引用存储在对象本身中?

IMO,最简单的解决方案是使用我在上面的评论中引用的Deferred对象,并避免对promise进行子类化。当你对promise进行子类化时会有一些复杂的问题,因为当有人在你的子类promise上调用.then().catch()时,这些函数会返回一个新的promise,系统会试图让这个新promise成为你的类。所以,除了你们自己的承诺行为外,你们的承诺子类必须完全支持常规的承诺行为。调试这些东西可能会非常非常令人困惑,因为你的构造函数被调用的次数比你想象的要多得多,因为所有系统创建的承诺都在构建中。

因此,我的建议是使用这个答案中解释的Deferred,它应该完全支持您想要做的事情,而不需要对promise进行任何复杂的子类化。


如果你真的想将promise子类化,你可以这样做:

class PromiseDeferred extends Promise {
constructor(executor) {
let res, rej;
super((resolve, reject) => {
res = resolve;
rej = reject;
});
this.resolve = res;
this.reject = rej;
// when someone uses .then() or .catch() on our PromiseDeferred
// that action creates a new promise and it will create one using
// our class.  It will, however, call our constructor with a
// regular executor function and expect it to work normally
// so we have to support that behavior
if (executor) {
executor(res, rej);
}
}
}
// sample usage
let d1 = new PromiseDeferred();
d1.then(val => {
console.log('Promise d1 resolved with', val);
}).catch(err => {
console.log(`Promise d1 rejected with message: ${err.message}`);
});
d1.resolve("Hello");
// -----------------------------------------
let d2 = new PromiseDeferred();
d2.then(val => {
console.log('Promise d2 resolved with', val);
}).catch(err => {
console.log(`Promise d2 rejected with message: ${err.message}`);
});
d2.reject(new Error("Promise rejected"));

性能

仅供参考,避免对promise进行子类化的另一个原因是,在使用这些子类promise时,您可能会丢失许多内置优化。当解释器处理一个它完全知道其行为的内置对象时,它通常可以进行一些性能优化,如果你将其子类化,这些优化将被跳过。我知道如果你将数组子类化的话,这是绝对正确的。对于细分的Array对象,某些操作可能会慢20倍。由于promise已经对它们进行了大量的性能优化,如果对它们进行子类化也会导致您丢失一些性能优化,我也不会感到惊讶。

您可以简单地将resolve/reject函数存储在promise对象本身中,以使它们可以访问。它们被传递给构造函数中的回调,因此您可以将自己的回调传递到那里,保存它们,然后调用原始回调。回调中尚未存在this的问题,可以通过将函数临时存储在本地变量中,并在返回对super的调用后仅将其分配给this来处理:

class Promise2 extends Promise {
constructor (callback) {
let tmp

super((resolve, reject) => {
tmp = { resolve, reject }

callback?.(resolve, reject)
})

Object.assign(this, tmp)
}
}

(我在函数调用上使用了可选的链接,以支持省略回调参数。(

最新更新