admin管理员组

文章数量:1327843

Let me start with the fact that I like asynchronous code. I would never wrap async code in a sync wrapper in production, but it is still something that I want to learn how to do. I am talking about specifically in Node.JS, not the browser. There are many ways to access the result of an async function synchronously like using child_process.spawnSync or a worker and Atomics. The issue with these ways is this:

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

Promises cannot be sent in a postMessage call, so a worker cannot access them and wait for them to finish synchronously or at all. One might think, why not do this:

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

Of course, this doesn't work. The event loop waits executes the while loop pletely before even beginning to deal with the callback function of prom.then. This causes an infinite loop. So this makes me ask, "Is there a synchronous task that has to execute not ordinarily to allow for the waiting of a promise?"

Edit
By the way, I totally understand async/await. If I am using a Node.JS module, then I can just do:

let resolved = await prom;

Or if I am not, I can wrap the whole script in an async function and do the same thing. However, the goal is to be able to get the access of a result avoiding await OR using await in an asynchronous context that is able to be accessed and waited for from a synchronous context.

Let me start with the fact that I like asynchronous code. I would never wrap async code in a sync wrapper in production, but it is still something that I want to learn how to do. I am talking about specifically in Node.JS, not the browser. There are many ways to access the result of an async function synchronously like using child_process.spawnSync or a worker and Atomics. The issue with these ways is this:

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

Promises cannot be sent in a postMessage call, so a worker cannot access them and wait for them to finish synchronously or at all. One might think, why not do this:

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

Of course, this doesn't work. The event loop waits executes the while loop pletely before even beginning to deal with the callback function of prom.then. This causes an infinite loop. So this makes me ask, "Is there a synchronous task that has to execute not ordinarily to allow for the waiting of a promise?"

Edit
By the way, I totally understand async/await. If I am using a Node.JS module, then I can just do:

let resolved = await prom;

Or if I am not, I can wrap the whole script in an async function and do the same thing. However, the goal is to be able to get the access of a result avoiding await OR using await in an asynchronous context that is able to be accessed and waited for from a synchronous context.

Share edited May 18, 2021 at 20:36 quicVO asked May 18, 2021 at 20:16 quicVOquicVO 8886 silver badges14 bronze badges 11
  • Unless you can invent a time machine... no – Kevin B Commented May 18, 2021 at 20:21
  • Not possible, and that is a good thing. Look however at await. – trincot Commented May 18, 2021 at 20:26
  • @KevinB, I was thinking it might be possible with something from the fs module. Also, what do you mean by "a time machine," there are a few ways to have node.js wait for async code to execute async. I saw this and was wondering if there was another way of doing it though. – quicVO Commented May 18, 2021 at 20:28
  • no, nothing that gets added to the callback queue can return to the scope that put it there. async/await can make it appear as though it does that, but behind the scenes it works similar to .then().then().then() – Kevin B Commented May 18, 2021 at 20:31
  • you could synchronously execute a child process that then runs more node js that does asynchronous stuff and then returns a value, technically, but really you're just pushing the asynchronous logic elsewhere. async/await can achieve the same end goal without the shenanigans/blocking. – Kevin B Commented May 18, 2021 at 20:34
 |  Show 6 more ments

2 Answers 2

Reset to default 6

This should not be possible. The ECMAScript specification forbids it.

First note that the ECMAScript specification speaks of "jobs" when referring to asynchronous code execution related to promises. For instance, at Promise Objects:

A promise p is fulfilled if p.then(f, r) will immediately enqueue a Job to call the function f.

Jobs are required to only execute when the call stack is empty. For instance, it specifies at Jobs and Host Operations to Enqueue Jobs:

A Job is an abstract closure with no parameters that initiates an ECMAScript putation when no other ECMAScript putation 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

You have pointed to Node's undocumented and deprecated process._tickCallback() method, which clearly violates the specification, as it allows job(s) in the promise job queue to execute while there is a non-empty call stack.

Using such constructs is thus not good practice, as other code cannot rely any more on the above principles of asynchronous job execution.

As to process._tickCallback() specifically: if it is used to allow a promise to call its then callbacks, this will not "work" when that promise depends on some other asynchronous API, as for instance setTimeout. For instance, the following code snippet will loop for ever, as the timeout Job never gets executed:

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

while(!result) process._tickCallback();

console.log("all done")

Asynchrony

In JavaScript asynchronous code runs (must run) when the call stack is empty. There should be no attempt to make it work differently. The coder should embrace this pattern fully.

I found the npm package deasync. In it, there is a line of code: process._tickCallback(). I implemented it as a function. This will only resolve a promise that has been pletely fulfilled. For example:

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

The feature could disappear whenever as it isn't included in documentation. If the promise is pending, then the while loop will be infinite.

本文标签: javascriptHow to access the result of a Promise synchronouslyStack Overflow