如何同步访问 Promise 的结果?

qui*_*cVO 6 javascript event-loop synchronous node.js promise

让我从我喜欢异步代码的事实开始。我永远不会在生产中将异步代码包装在同步包装器中,但这仍然是我想学习如何做的事情。我指的是 Node.JS,而不是浏览器。有很多方法可以同步访问异步函数的结果,例如使用child_process.spawnSync或工作器和Atomics。这些方法的问题是:

let prom = Promise.resolve(4);
// It is now impossible (as far as I know) to access the result of prom synchronously
Run Code Online (Sandbox Code Playgroud)

Promise 无法在调用中发送postMessage,因此工作人员无法访问它们并等待它们同步完成或根本无法完成。有人可能会想,为什么不这样做:

let prom = Promise.resolve(4);
prom.then(res => global.result = res);
while (!global.result) {}; // Do nothing
// Once the loop finishes, the result *would* be available
console.log(global.result); // => 4
Run Code Online (Sandbox Code Playgroud)

当然,这是行不通的。事件循环在开始处理 的回调函数之前等待完全执行 while 循环prom.then。这会导致无限循环。所以这让我问,“是否有一个同步任务必须非正常执行才能等待承诺?”

编辑
顺便说一句,我完全理解async/await。如果我使用 Node.JS 模块,那么我可以这样做:

let resolved = await prom;
Run Code Online (Sandbox Code Playgroud)

或者,如果我不是,我可以将整个脚本包装在一个async函数中并执行相同的操作。但是,目标是能够访问结果,避免使用等待或在能够从同步上下文访问和等待的异步上下文中使用等待。

tri*_*cot 6

这应该是不可能的。ECMAScript 规范禁止这样做。

首先请注意,ECMAScript 规范在提到与 Promise 相关的异步代码执行时谈到“作业”。例如,在Promise Objects中:

如果立即将作业加入队列以调用该函数,则承诺p已实现。p.then(f, r)f

要求作业仅在调用堆栈为空时执行。例如,它在作业和主机操作中指定将作业排队

Job 是一个没有参数的抽象闭包,当当前没有其他 ECMAScript 计算正在进行时,它会启动 ECMAScript 计算。

它们的实现必须符合以下要求:

  • 在未来的某个时间点,当没有正在运行的执行上下文并且执行上下文堆栈为空时,实现必须:
    • [...]调用抽象闭包

您已经指出 Node 的未记录和已弃用的 process._tickCallback()方法,这显然违反了规范,因为它允许承诺作业队列中的作业在存在非空调用堆栈时执行。

因此,使用此类构造并不是一个好的做法,因为其他代码不能再依赖上述异步作业执行的原则。

具体而言process._tickCallback():如果它用于允许 Promise 调用其then回调,那么当该 Promise 依赖于某些其他异步 API(例如setTimeout. 例如,以下代码片段将永远循环,因为超时作业永远不会被执行:

let result = 0;
new Promise(resolve => 
    setTimeout(resolve, 10)
).then(() => result = 1);

while(!result) process._tickCallback();

console.log("all done")
Run Code Online (Sandbox Code Playgroud)

异步

在 JavaScript 中,异步代码在调用堆栈为空时运行(必须运行)。不应该试图让它以不同的方式工作。编码人员应该完全接受这种模式。

  • 感谢您抽出时间回答这个问题。我知道我的问题“不受欢迎”。我并不关心良好的实践,但你帮助我明白了为什么我永远不会使用它。Node.js 为开发人员提供了一种同步执行异步任务的方法,但这并不意味着您应该这样做。尝试“规避”规则时不会发生任何伟大的事情,所以我可能永远不会使用它。由于您的解释,我将您的答案标记为已接受的答案,但也会留下我的答案。:) (2认同)