javascript-自定义Promise类的构造函数被调用两次(扩展了标准Promise)
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了javascript-自定义Promise类的构造函数被调用两次(扩展了标准Promise),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5134字,纯文字阅读大概需要8分钟。
内容图文
![javascript-自定义Promise类的构造函数被调用两次(扩展了标准Promise)](/upload/InfoBanner/zyjiaocheng/696/dd8a612eb7aa480f8510d57d2accb469.jpg)
我正在使用Promise Extensions for JavaScript (prex),我想使用prex.CancellationToken、complete code here扩展具有取消支持的标准Promise class.
出乎意料的是,我看到自定义类CancellablePromise的构造函数被调用了两次.为简化起见,我现在简化了所有取消逻辑,只保留了重现此问题所需的最低限度:
class CancellablePromise extends Promise {
constructor(executor) {
console.log("CancellablePromise::constructor");
super(executor);
}
}
function delayWithCancellation(timeoutMs, token) {
// TODO: we've stripped all cancellation logic for now
console.log("delayWithCancellation");
return new CancellablePromise(resolve => {
setTimeout(resolve, timeoutMs);
}, token);
}
async function main() {
await delayWithCancellation(2000, null);
console.log("successfully delayed.");
}
main().catch(e => console.log(e));
使用节点simple-test.js运行它,得到的是:
delayWithCancellation CancellablePromise::constructor CancellablePromise::constructor successfully delayed.
为什么有两个CancellablePromise :: constructor调用?
我尝试使用VSCode设置断点.第二个命中的堆栈跟踪显示它是从runMicrotasks调用的,它本身是从Node内的_tickCallback调用的.
更新后,Google现在拥有“await under the hood”博客,这是一本不错的书,有助于您理解此行为以及V8中其他一些异步/等待实现的细节.
解决方法:
第一次更新:
我首先想到’main’之后的.catch(callback)将返回扩展的Promise类的新的,未决的Promise,但这是不正确的-调用异步函数将返回Promise Promise.
进一步削减代码,仅产生未完成的承诺:
class CancellablePromise extends Promise {
constructor(executor) {
console.log("CancellablePromise::constructor");
super(executor);
}
}
async function test() {
await new CancellablePromise( ()=>null);
}
test();
显示了在Firefox,Chrome和Node中被两次调用的扩展构造函数.
现在等待在其操作数上调用Promise.resolve. (编辑:或者它可能是在未严格按照标准实现的早期JS引擎版本的async / await中执行的)
如果操作数是其构造函数为Promise的Promise,则Promise.resolve将不变地返回操作数.
如果操作数是一个其构造函数不是Promise的thenable,则Promise.resolve会同时使用onfulfilled和onRejected处理函数调用该操作数的then方法,以便通知该操作数的稳定状态.然后,通过此调用创建并返回的promise是扩展类的,并负责了对CancellablePromise.prototype.constructor的第二次调用.
支持证据
>新的CancellablePromise().构造函数为CancellablePromise
class CancellablePromise extends Promise {
constructor(executor) {
super(executor);
}
}
console.log ( new CancellablePromise( ()=>null).constructor.name);
>将出于测试目的将CancellablePromise.prototype.constructor更改为Promise只会导致一次对CancellablePromise的调用(因为await被欺骗返回其操作数):
class CancellablePromise extends Promise {
constructor(executor) {
console.log("CancellablePromise::constructor");
super(executor);
}
}
CancellablePromise.prototype.constructor = Promise; // TESTING ONLY
async function test() {
await new CancellablePromise( ()=>null);
}
test();
第二次更新(非常感谢OP提供的链接)
符合规范的实施
await使用onFulilled和onRejected处理程序创建一个匿名的,中间的Promise承诺
在等待操作符之后恢复执行或从中引发错误,具体取决于中间承诺实现的结算状态.
然后,它(等待)还调用操作数承诺来实现或拒绝中间承诺.然后,此特定调用将返回一个类operandPromise.constructor的承诺.尽管从不使用随后返回的Promise,但在扩展类构造函数中进行登录将显示该调用.
如果出于实验目的将扩展promise的构造函数值更改回Promise,则上述then调用将静默返回Promise类promise.
附录:解密await specification
Let asyncContext be the running execution context.
Let promiseCapability be ! NewPromiseCapability(%Promise%).
创建一个具有Promise,Resolve和拒绝属性的类似jQuery的新延迟对象,将其称为“ PromiseCapability Record”.延迟的promise对象属于(全局)基本Promise构造函数类.
- Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
用正确的等待操作数解决延迟的诺言.解析过程或者调用操作数的then方法(如果它是“ thenable”),或者如果操作数是其他非承诺值,则履行递延的承诺.
Let stepsFulfilled be the algorithm steps defined in Await Fulfilled Functions.
Let onFulfilled be CreateBuiltinFunction(stepsFulfilled, « [[AsyncContext]] »).
Set onFulfilled.[[AsyncContext]] to asyncContext.
通过返回作为参数传递给处理程序的操作数的完成值,创建一个onfulfilled处理程序以在调用它的异步函数内部恢复等待操作.
Let stepsRejected be the algorithm steps defined in Await Rejected Functions.
Let onRejected be CreateBuiltinFunction(stepsRejected, « [[AsyncContext]] »).
Set onRejected.[[AsyncContext]] to asyncContext.
通过抛出传递给处理程序作为其参数的承诺拒绝原因,创建一个onrejected处理程序以在调用它的异步函数内恢复await操作.
- Perform ! PerformPromiseThen(promiseCapability.[[Promise]], onFulfilled, onRejected).
然后使用这两个处理程序调用延迟的Promise,以便await可以响应其操作数已结算.
使用三个参数进行的此调用是一种优化,可以有效地进行内部调用,并且不会在调用中创建或返回承诺.因此,延迟的结算将调度其结算处理程序之一调用到promise作业队列中执行,但是没有其他副作用.
Remove asyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
Set the code evaluation state of asyncContext such that when evaluation is resumed with a Completion completion, the following steps of the algorithm that invoked Await will be performed, with completion available.
存储成功等待后要恢复的位置,然后返回到事件循环或微任务队列管理器.
内容总结
以上是互联网集市为您收集整理的javascript-自定义Promise类的构造函数被调用两次(扩展了标准Promise)全部内容,希望文章能够帮你解决javascript-自定义Promise类的构造函数被调用两次(扩展了标准Promise)所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。