如何解决函数返回后,JavaScript 调用堆栈中的变量如何仍可访问
混淆的两点:
- 如何保存和处理函数框架?
示例:
function foo() {
var a = ...;
setTimeout(function() {
console.log(a);
},50);
return a;
}
在这个例子中,setTimeout 中的内部函数正在引用外部变量 a,即使在 foo 返回之后也是如此。来自 Java 世界,我很困惑这怎么会发生? foo 堆栈帧如何存储以供内部函数使用,以及何时从堆栈中弹出?
- 多次异步/承诺“回报”
示例:
async function foo2() {
var p = new Promise() {
setTimeout(function() {
p.reject(null);
},60000);
p.resolve(await dbcall.execute());
}
return p;
}
还有其他地方:
foo2.then(resolve,reject) {
...
}
假设首先发生超时调用,然后 dbcall 返回。承诺可能会被解决两次。
问题:在超时调用被拒绝后,该函数是否仍然(a)等待db调用返回,然后执行await之后的任何代码?第二次履行承诺会发生什么(即等待完成时的解决调用)是否只有第一个解决/拒绝得到处理?
解决方法
来自 Java 世界的我很困惑这怎么会发生?
这叫做闭包。
一个函数引用的所有变量都保留在该函数的作用域内,无论它在哪里,或何时执行。这是 JavaScript 的基本原则之一。
你的第二个例子没有意义。
- 将函数声明为
async
的唯一原因是您想在内部使用await
。 - 如果内部函数返回一个承诺,则返回该承诺。不要创建新的(请参阅the Promise construction antipattern)。
所以它应该简单地阅读:
function foo2() {
return dbcall.execute();
}
或者,在粗箭头语法中
const foo2 = () => dbcall.execute();
为了产生超时,您可以使用 Promise.race()
。
const timeout = (promise,ms) => Promise.race([
new Promise((_,reject) => setTimeout(() => reject('timeout'),ms)),promise
]);
timeout(foo2(),60000).then(foo2Result => {
// success
}).catch(error => {
// either 'timeout' or an error from dbcall
});
但是如果你想将它包装在一个新的 promise 中,你可以 - 以牺牲可组合性为代价:
const foo2 = () => new Promise((resolve,reject) => {
dbcall.execute().then(resolve).catch(reject);
setTimeout(() => reject('timeout'),60000);
});
超时调用被拒绝后,函数是否仍然保持(a)等待db调用返回
是的,这两件事都是独立执行的。除非您建立相互取消机制,否则 setTimeout()
和 dbcall.execute()
都会运行完成。
第二次履行承诺会发生什么(即等待完成时的解析调用)是否只有第一个解析/拒绝被处理?
无论有多少内部函数在事后调用 reject
或 resolve
,已解决(已解决或被拒绝)的承诺都不会再更改其最终值。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。