推理很简单,但不一定是不言而喻的。
-
.then() 返回一个承诺
- 如果在 Promise 的子类上调用
then,则返回的 Promise 是子类的实例,而不是 Promise 本身。
-
then 返回的 Promise 是通过调用子类构造函数构造的,并向其传递一个内部执行函数,该函数记录传递给它的 resolve 和 reject 参数的值以供以后使用。
- “稍后使用”包括在监视
onfulfilled 或 onrejected 处理程序(稍后)的执行以查看它们是否返回值(这解决了 then 返回的承诺时)异步解决或拒绝 then 返回的承诺) 或抛出错误(拒绝承诺)。
简而言之,then 调用在内部获取并记录对它们返回的 Promise 的 resolve 和 reject 函数的引用。
所以关于这个问题,
new MyPromise( 'p1')
工作正常,是对子类构造函数的第一次调用。
.then( someFunction)
将someFunction 记录在对new MyPromise 进行的then 调用列表中(调用then 可以多次调用)并尝试通过调用来创建返回承诺
new MyPromise( (resolve, reject) => ... /* store resolve reject references */
这是来自then 代码的对子类构造函数的第二次调用。构造函数应该(并且确实)同步返回。
在从创建返回的承诺返回时,.then 方法会进行完整性检查,以查看它需要供以后使用的 resolve 和 reject 函数是否实际上是函数。它们应该与then 调用中提供的回调一起存储(在列表中)。
在MyPromise 的情况下,它们不是。通过then 传递给MyPromise 的执行程序甚至没有被调用。所以then 方法代码会抛出一个类型错误“Promise resolve or reject function is not callable”——它没有办法解决或拒绝它应该返回的 Promise。
在创建 Promise 的子类时,子类构造函数必须将执行器函数作为其第一个参数,并使用真正的 resolve 和 reject 函数参数调用执行器。这是then 方法代码内部需要的。
使用MyPromise 做一些复杂的事情,也许检查第一个参数以查看它是否是一个函数,如果是,则将其作为执行程序调用,可能是可行的,但超出了此答案的范围!对于显示的代码,编写工厂/库函数可能更简单:
function namedDelay(name, delay=1000, value=1) {
var promise = new Promise( (resolve,reject) => {
setTimeout(() => {
resolve(value)
}, delay)
}
);
promise.name = name;
return promise;
}
namedDelay( 'p1')
.then(result => {
console.log('fulfilled, result: ', result)
})
.catch(err => {
console.error('err: ', err)
})
;TLDR
Promise 的类扩展不是扩展。如果是,则需要实现 Promise 接口并将执行器函数作为第一个参数。您可以使用工厂函数返回异步解析的 Promise(如上),或者使用 hack 发布的代码
MyPromise.prototype.constructor = Promise
这会导致.then 返回一个常规的 Promise 对象。 hack 本身驳斥了正在发生类扩展的想法。
Promise 扩展示例
以下示例显示了一个基本的 Promise 扩展,它添加了提供给构造函数的属性。注意:
class MyPromise extends Promise {
constructor(exec, props) {
if( typeof exec != "function") {
throw TypeError( "new MyPromise(executor, props): an executor function is required");
}
super((resolve, reject) => exec(resolve,reject));
if( props) {
Object.assign( this, props);
}
}
get [Symbol.toStringTag]() {
return 'MyPromise';
}
}
// Test the extension:
const p1 = new MyPromise( (resolve, reject) =>
resolve(42),
{id: "p1", bark: ()=>console.log("woof") });
console.log( "p1 is a %s object", p1.constructor.name);
console.log( "p1.toString() = %s", p1.toString());
console.log( "p1.id = '%s'", p1.id);
console.log( "p1 says:"); p1.bark();
const pThen = p1.then(data=>data);
console.log( "p1.then() returns a %s object", pThen.constructor.name);
let pAll = MyPromise.all([Promise.resolve(39)]);
console.log( "MyPromise.all returns a %s object", pAll.constructor.name);
try { new MyPromise(); }
catch(err) {
console.log( "new MyPromise() threw: '%s'", err.message);
}