JavaScript 中的调用堆栈中的变量如何在函数 returns 之后仍然可以访问
How are variables in a call stack in JavaScript still accessible after the function returns
两点困惑:
- 函数框架是如何保存和处理的?
示例:
function foo() {
var a = ...;
setTimeout(function() {
console.log(a);
},50);
return a;
}
在此示例中,setTimeout 中的内部函数正在引用外部变量 a,即使在 foo returned 之后也是如此。来自 Java 世界,我对这怎么会发生感到困惑? foo 堆栈帧如何存储以供内部函数使用,以及何时从堆栈中弹出?
- 多个async/promise“returns”
示例:
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 returns。该承诺可能会被解决两次。
Question(s): 超时调用拒绝后,该函数是否仍会 (a) 等待对 return 的数据库调用,然后执行 await 之后的任何代码?第二次履行承诺会发生什么(即 await 完成时的 resolve 调用)是否只处理第一个 resolve/reject?
Coming from the Java world, I am confused as to how this can happen?
这叫做闭包。
函数引用的所有变量都保留在该函数的范围内,无论它在何处或何时执行。这是 JavaScript.
中的基本原则之一
你的第二个例子没有意义。
- 将函数声明为
async
的唯一原因是如果您想在内部使用 await
。
- 如果内部函数 return 是一个承诺,return 那个承诺。不要创建新的(参见 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
});
但是如果你想将它包装在一个新的承诺中,你可以 - 以牺牲可组合性为代价:
const foo2 = () => new Promise((resolve, reject) => {
dbcall.execute().then(resolve).catch(reject);
setTimeout(() => reject('timeout'), 60000);
});
After the timeout call rejects, will the function still keep (a)waiting for db call to return
是的,这两件事都是独立执行的。除非你建立相互取消机制,否则 setTimeout()
和 dbcall.execute()
都会 运行 完成。
what happens with a second fulfillment of the promise (ie the resolve call when await has completed) Does only the first resolve/reject get processed?
无论有多少内部函数在事后调用 reject
或 resolve
,已解决(解决或拒绝)的承诺将不再更改其最终值。
两点困惑:
- 函数框架是如何保存和处理的?
示例:
function foo() {
var a = ...;
setTimeout(function() {
console.log(a);
},50);
return a;
}
在此示例中,setTimeout 中的内部函数正在引用外部变量 a,即使在 foo returned 之后也是如此。来自 Java 世界,我对这怎么会发生感到困惑? foo 堆栈帧如何存储以供内部函数使用,以及何时从堆栈中弹出?
- 多个async/promise“returns”
示例:
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 returns。该承诺可能会被解决两次。
Question(s): 超时调用拒绝后,该函数是否仍会 (a) 等待对 return 的数据库调用,然后执行 await 之后的任何代码?第二次履行承诺会发生什么(即 await 完成时的 resolve 调用)是否只处理第一个 resolve/reject?
Coming from the Java world, I am confused as to how this can happen?
这叫做闭包。
函数引用的所有变量都保留在该函数的范围内,无论它在何处或何时执行。这是 JavaScript.
中的基本原则之一你的第二个例子没有意义。
- 将函数声明为
async
的唯一原因是如果您想在内部使用await
。 - 如果内部函数 return 是一个承诺,return 那个承诺。不要创建新的(参见 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
});
但是如果你想将它包装在一个新的承诺中,你可以 - 以牺牲可组合性为代价:
const foo2 = () => new Promise((resolve, reject) => {
dbcall.execute().then(resolve).catch(reject);
setTimeout(() => reject('timeout'), 60000);
});
After the timeout call rejects, will the function still keep (a)waiting for db call to return
是的,这两件事都是独立执行的。除非你建立相互取消机制,否则 setTimeout()
和 dbcall.execute()
都会 运行 完成。
what happens with a second fulfillment of the promise (ie the resolve call when await has completed) Does only the first resolve/reject get processed?
无论有多少内部函数在事后调用 reject
或 resolve
,已解决(解决或拒绝)的承诺将不再更改其最终值。