如何同步访问 Promise 的结果?
How to access the result of a Promise synchronously?
让我先从我喜欢异步代码这一事实说起。我永远不会在生产中将异步代码包装在同步包装器中,但这仍然是我想学习如何做的事情。我说的是 Node.JS,而不是浏览器。有很多方法可以同步访问异步函数的结果,例如使用 child_process.spawnSync
或 worker 和 Atomics
。这些方法的问题是:
let prom = Promise.resolve(4);
// It is now impossible (as far as I know) to access the result of prom synchronously
无法在 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
当然,这不行。事件循环甚至在开始处理prom.then
的回调函数之前就等待执行完while循环。这会导致无限循环。所以这让我问,“是否有同步任务 必须 通常不执行以允许等待承诺?”
编辑
顺便说一句,我完全理解async/await
。如果我使用的是 Node.JS 模块,那么我可以这样做:
let resolved = await prom;
或者如果我不是,我可以将整个脚本包装在一个 async
函数中并执行相同的操作。但是,目标是能够访问结果,避免等待或在异步上下文中使用 await,从而能够从同步上下文访问和等待。
我找到了 npm 包 deasync。其中,有一行代码:process._tickCallback()
。我将它实现为一个函数。这将仅 解决已完全实现 的承诺。例如:
function halt(promise) {
let result;
promise.then(res=>result=res);
while(!result) process._tickCallback();
return result;
};
let ans = halt(Promise.resolve(123));
console.log(ans); //=> 123
该功能可能会随时消失,因为它没有包含在文档中。如果 promise 未决,则 while 循环将是无限的。
这应该是不可能的。 ECMAScript 规范禁止它。
首先请注意,ECMAScript 规范在提到与承诺相关的异步代码执行时会提到“作业”。例如,在 Promise Objects:
A promise p
is fulfilled if p.then(f, r)
will immediately enqueue a Job to call the function f
.
作业需要仅在调用堆栈为空时执行。例如,它指定 Jobs and Host Operations to Enqueue Jobs:
A Job is an abstract closure with no parameters that initiates an ECMAScript computation when no other ECMAScript computation is currently in progress.
Their implementations must conform to the following requirements:
- At some future point in time, when there is no running execution context and the execution context stack is empty, the implementation must:
- [...] Call the abstract closure
您指出了 Node 的未记录和 deprecated process._tickCallback()
方法,这显然违反了规范,因为它允许 promise 作业队列中的作业在有非清空调用堆栈。
因此,使用此类构造不是好的做法,因为其他代码不能再依赖上述异步作业执行原则。
具体到 process._tickCallback()
:如果它用于允许承诺调用其 then
回调,则当该承诺依赖于其他一些异步 API,例如setTimeout
。例如,以下代码片段将永远循环,因为超时作业永远不会执行:
let result = 0;
new Promise(resolve =>
setTimeout(resolve, 10)
).then(() => result = 1);
while(!result) process._tickCallback();
console.log("all done")
异步
在JavaScript异步代码运行s(必须运行)时调用栈为空。不应该试图让它以不同的方式工作。编码人员应该完全接受这种模式。
让我先从我喜欢异步代码这一事实说起。我永远不会在生产中将异步代码包装在同步包装器中,但这仍然是我想学习如何做的事情。我说的是 Node.JS,而不是浏览器。有很多方法可以同步访问异步函数的结果,例如使用 child_process.spawnSync
或 worker 和 Atomics
。这些方法的问题是:
let prom = Promise.resolve(4);
// It is now impossible (as far as I know) to access the result of prom synchronously
无法在 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
当然,这不行。事件循环甚至在开始处理prom.then
的回调函数之前就等待执行完while循环。这会导致无限循环。所以这让我问,“是否有同步任务 必须 通常不执行以允许等待承诺?”
编辑
顺便说一句,我完全理解async/await
。如果我使用的是 Node.JS 模块,那么我可以这样做:
let resolved = await prom;
或者如果我不是,我可以将整个脚本包装在一个 async
函数中并执行相同的操作。但是,目标是能够访问结果,避免等待或在异步上下文中使用 await,从而能够从同步上下文访问和等待。
我找到了 npm 包 deasync。其中,有一行代码:process._tickCallback()
。我将它实现为一个函数。这将仅 解决已完全实现 的承诺。例如:
function halt(promise) {
let result;
promise.then(res=>result=res);
while(!result) process._tickCallback();
return result;
};
let ans = halt(Promise.resolve(123));
console.log(ans); //=> 123
该功能可能会随时消失,因为它没有包含在文档中。如果 promise 未决,则 while 循环将是无限的。
这应该是不可能的。 ECMAScript 规范禁止它。
首先请注意,ECMAScript 规范在提到与承诺相关的异步代码执行时会提到“作业”。例如,在 Promise Objects:
A promise
p
is fulfilled ifp.then(f, r)
will immediately enqueue a Job to call the functionf
.
作业需要仅在调用堆栈为空时执行。例如,它指定 Jobs and Host Operations to Enqueue Jobs:
A Job is an abstract closure with no parameters that initiates an ECMAScript computation when no other ECMAScript computation is currently in progress.
Their implementations must conform to the following requirements:
- At some future point in time, when there is no running execution context and the execution context stack is empty, the implementation must:
- [...] Call the abstract closure
您指出了 Node 的未记录和 deprecated process._tickCallback()
方法,这显然违反了规范,因为它允许 promise 作业队列中的作业在有非清空调用堆栈。
因此,使用此类构造不是好的做法,因为其他代码不能再依赖上述异步作业执行原则。
具体到 process._tickCallback()
:如果它用于允许承诺调用其 then
回调,则当该承诺依赖于其他一些异步 API,例如setTimeout
。例如,以下代码片段将永远循环,因为超时作业永远不会执行:
let result = 0;
new Promise(resolve =>
setTimeout(resolve, 10)
).then(() => result = 1);
while(!result) process._tickCallback();
console.log("all done")
异步
在JavaScript异步代码运行s(必须运行)时调用栈为空。不应该试图让它以不同的方式工作。编码人员应该完全接受这种模式。