JavaScript函数返回后调用栈变量可访问、栈帧处理及Promise多决议疑问
问题1:函数帧的保存与销毁规则
你有Java开发背景的话可以先做个简单类比:Java中方法栈帧弹栈后,栈上存储的基本类型会直接销毁,堆上对象只要没有GC根引用才会被回收,JS的逻辑核心和这个是共通的,差异在于JS的闭包捕获机制。
你给出的示例代码:
function foo() { var a = ...; setTimeout(function() { console.log(a); },50); return a; }
执行逻辑对应的规则如下:
foo执行时会在调用栈上创建对应的执行上下文(也就是你说的栈帧),变量a属于foo的词法作用域,当foo执行到return语句时,它的执行上下文会立刻从调用栈弹出,这一点和Java方法返回弹栈的逻辑完全一致。- 由于
setTimeout传入的匿名函数引用了外部作用域的a,JS引擎会判定a被闭包捕获,不会随着foo上下文弹栈就销毁,而是把a所在的词法环境对象转移到堆内存中,由待执行的定时器回调持有引用。 - 直到定时器回调执行完成,没有其他引用指向这个词法环境对象时,
a和对应的作用域才会被垃圾回收器清理。
问题2:多个async/promise"返回"的处理规则
首先补充说明:你给出的示例代码存在语法小问题,Promise构造函数的标准写法是new Promise((resolve, reject) => { ... }),但不影响问题核心逻辑的讨论。
针对你提出的疑问逐一解答:
- timeout触发reject后,函数是否还会继续等待dbcall返回,执行await后的代码?
会。await dbcall.execute()只会暂停foo2函数的执行,等待dbcall本身的异步操作决议,这个逻辑不受外部p的状态影响。就算p已经被reject,dbcall的IO/网络请求还是会正常执行到结束,返回后await后的p.resolve代码也会正常触发。 - promise的第二次决议会如何处理?是否只有第一次resolve/reject会生效?
Promise的状态是不可逆的,一旦从pending状态变为fulfilled或rejected,后续所有对同一个Promise的resolve/reject调用都会被引擎直接忽略,不会改变Promise状态,也不会触发额外的then/catch回调。你场景中timeout先触发了p.reject(null),p已经变为rejected状态,后续dbcall返回后的p.resolve调用没有任何实际效果。
额外注意:Promise状态不可逆不代表异步操作会被中断,
dbcall.execute如果持有数据库连接、文件句柄等资源,还是会正常运行到结束,如果你需要提前取消这类操作,需要单独实现取消逻辑(比如基于AbortController实现)。
内容的提问来源于stack exchange,提问作者ControlAltDel
相关产品推荐
相关产品推荐

