nodejs / node

Node.js JavaScript runtime ✨🐢🚀✨
https://nodejs.org
Other
107.8k stars 29.7k forks source link

providing another way for synchronise code to interface with asynchronise code #30634

Closed TrevTheDev closed 4 years ago

TrevTheDev commented 4 years ago

Node's async await features are awesome, however, they do not provide a way to interface with calls that were not designed to work with async-await in mind. For example, proxies enable one to trap get requests, however, there is no way to do synchronise calls and then supply the result of those synchronise calls to the get request, unless the caller implements await.

One possible solution is the deasync library:

  get: () => {
    let result;
    (async ()=>{  
        result = await doSyncCalls(target);
     })();
     deasync.loopWhile(() => !result);
     return result;
  }

However, deasync has a bug where nested promises fail to resolve.

This feature is required whenever one is unable to alter the caller's code to work with await. So any third-party libraries that calls ones code, but which are not designed to be synchronise. Without this feature, there is no good way to respond to those libraries.

Node already recognises this problem by providing sync versions of some of its api calls, e.g. execSync, spawnSync etc. Node provides this functionality to its users, but they are unable to do similar to their users.

Without this feature, there is a whole set of situation that node will be unable to respond to.

devsnek commented 4 years ago

Node's sync variants are separate C++ calls, they aren't just deasync'd versions of the async APIs.

At a very general level though, it's not possible to arbitrarily turn async apis into sync apis. I'd also say that anything relying on deasync is not something I would feel comfortable depending on.

yorkie commented 4 years ago

For your example, I just figure out an easier way than deasync:

new Proxy(..., {
  get: function() {
    return child_process.execSync('your actual async works', 'utf8');
  }
});

It's working in sync and no any extra syntax is required, but it seems weird a bit just like what you have written in your example at Node.js, which the best practice is not going to block others. Another solution is to return promise at traps, and document this in package README:

const foobar = await mDelegatedObj.foobar;
// or
mDelegatedObj.foobar.then(() => ...);
yorkie commented 4 years ago

And it's better to transfer the discussion to our nodejs/help repo, because this seems not related in Node.js core too much :)

TrevTheDev commented 4 years ago

@yorkie, yes your better example is what needs replication, except for for a broader set of functions than child_process.execSync. This would enable others to write code that mimics execSync in their code and write their own sync functions.

TrevTheDev commented 4 years ago

At a very general level though, it's not possible to arbitrarily turn async apis into sync apis.

Yes and that is why this feature is required, it could be something like

 const x = pause-await doSyncCalls(somVar)

That unlike await doesn't return a promise, but rather pauses execution in a non blocking way until the await is completed. Enabling an easy transition from sync to async.

yorkie commented 4 years ago

This would enable others to write code that mimics execSync in their code and write their own sync functions.

Node.js should be focusing on event-based IO which means async, maybe another v8-based runtime for that purpose?

devsnek commented 4 years ago

if you're asking for new language features, you should head over to https://tc39.es. Node.js can't add new language features.

TrevTheDev commented 4 years ago

@devsnek, yes it could be a language feature, it could also be a feature node provides, or it could be a library such as deasync. Possibly the simplest solution is to enhance Node to enable deasync to actually work - from their github issue, it seems the Node team rejected their change.

Without this feature going from sync to async is fine, but going from async to sync is undoable. That would be fine if people never needed to do so, but even Node provides sync functions demonstrating the use-case.

devsnek commented 4 years ago

I should've been a bit clearer. It's not possible to take an arbitrary function that performs some unknown async behaviour and make it synchronous. deasync exploits the fact that the majority of async behaviour in node applications is very similar: one-shot calls to libuv apis. However, as with the bug you linked, not everything follows that pattern.

Node is able to provide sync apis because it has specialized C++ code for those individual synchronous apis. We don't have an internal magic "syncify" api that could be exposed.

addaleax commented 4 years ago

Also, just to be clear on one point: What deasync does is not officially supported by Node.js in any way. I would recommend against using it whenever possible.

devsnek commented 4 years ago

I don't think there's anything actionable here, I'm going to close this out.