35

I'm dealing with a NodeJs framework that requires a certain function to be synchronous, but I need to retrieve a value that can only be accessed asynchronously. In a perfect world, I would be able to return a promise, but I can't.

As a quick-and-dirty solution, I created the following method:

exports.synchronizePromise = function(promise) {
    var value;
    promise.then(function(promiseValue) {
        value = promiseValue;
    });
    while (!value) {} // Wait for promise to resolve
    console.log("DONE: " + value); // Never reached
    return value;
};

But I get an error. Is there any way to accomplish what I need?

sinθ
  • 11,093
  • 25
  • 85
  • 121
  • 4
    promises don't magically make things sync, they just make some async processes easier to code and manage through multiple steps. don't use a loop to try to freeze, just fix the problem upstream by breaking whatever is sync into two parts. – dandavis Jun 11 '14 at 18:59
  • 2
    @dandavis I considered making a commit to the framework I'm using to make it so the function can be async, but it's called in too many places and 99% of the time, it doesn't need to by async. It's not really an option to fix it upstream. – sinθ Jun 11 '14 at 19:06
  • you can immediately return a guid after dispatching the process, and when the process completes, find the guid in your data/dom and replace for the actual value. – dandavis Jun 11 '14 at 19:07
  • 1
    Javascript (and NodeJS) has only a single thread, `while (!value) {}` will just freeze the program! (as @dandavis said). You'll have to get the value first, then do the synchronous part when you have it. – Matthew Wilcoxson Jun 11 '14 at 19:08
  • Why call the sync function, then call the async function inside of it? Calling the async function inside of it makes it no longer sync. You can't have it both ways. Get the async data first, then call the sync function. There is no other way. – Charlie Martin Jun 11 '14 at 19:09
  • @MatthewWilcoxson Oh right; I forgot about the single threaded part. That makes sense. The synchronous function is called by the framework (It's a function I override), so I don't really have an opportunity to retrieve the value asynchronously first. So I'm not sure what to do. – sinθ Jun 11 '14 at 19:10
  • The framework sounds like a poor match for NodeJS. Can we see the framework and the function you need? – Matthew Wilcoxson Jun 11 '14 at 19:14
  • @sinθ: you can replace the method itself, unless they called Object.freeze(), which is doubtful. you can wrap the orig and use closure to reach the old one internally, or just copy and modify the function and clobber the orig with your modified one, kinda like patching a binary. if the code uses closures, you have to parasitically wrap. – dandavis Jun 11 '14 at 19:14
  • @MatthewWilcoxson It's the toJSON method, which is used by the framework for the REST API. There's awkward workarounds I can (and have to) use. – sinθ Jun 11 '14 at 19:21
  • This is what I use to resolve similar problem but my Node.js framework is not expected to work in a high-performance web server → http://stackoverflow.com/search?q=I%27ve+recently+created+simpler+abstraction+WaitFor+to+call+async+functions+in+sync+mode – xmojmr Jan 07 '15 at 12:04

3 Answers3

4

Given that node is by default only single threaded there isn't really an easy way to solve this. There is one though. Bases on generators/fibers you can add a sort of concurrent execution to node. There is a waitfor implementation based on that.

tcurdt
  • 14,518
  • 10
  • 57
  • 72
3

In Q if you have a resolved promise you can just take the value with inspect

exports.synchronizePromise = function(promise) {
  var i = promise.inspect();
    if (i.state === "rejected") {
      throw i.reason;
    } else if (i.state === "fulfilled") {
      return i.value;
    } else {
      throw new Error("attempt to synchronize pending promise")
    }
};

However if the promise is pending, it is truly asynchronous and your question doesn't then make sense and the function will throw an error.

Esailija
  • 138,174
  • 23
  • 272
  • 326
-4

Promises are asynchronous. They represent a future value--a value available in 1ms, or 1 minute, or 1 day, or 1 year in the future, or never. By definition, there is no way to "force" them to resolve, other than a time machine.

If you have upstream logic which is built on the premise of synchronicity, but for whatever reason things it was depending on are now asynchronous, you have to refactor that upstream component to operate asynchronously as well. There is no other alternative.

If your framework "requires a certain function to be synchronous" when that function is not or cannot be synchronous, then the framework is poorly designed and unusable, or at least not usable for your problem.

  • 14
    While a valid point, it is tangential to the actual question, which is about resolving promises. Resolving promises have a value that go beyond this particular use case. – Siddhu Aug 21 '15 at 09:55
  • @siddhuwarrier Not quite sure what you are trying to say. Basically, you can't resolve a promise; the promise resolves itself (or doesn't) and all you can do is wait for that to happen. Or am I missing something? What do you mean by "resolving promises"? –  May 27 '16 at 16:56
  • What I was referring to was a mechanism whereby you can wait for the result of an asynchronous operation to resolve. For instance, in Scala, ` val f = Future { ... }; f.map { item => //perform action with future result }.recover { //handle errors }; Await.result(f, 10000 milliseconds); ` One use case for something like this is that the future itself could end up executing multiple threads in parallel to produce a result, while the main thread waits for it to finish. – Siddhu May 28 '16 at 19:05
  • 1
    While Javascript's promises may not support this, it does not mean there aren't any good reasons why you would need a thread to block on the result of an asynchronous operation. – Siddhu May 28 '16 at 19:08
  • 2
    Thread joins/blocking awaits are supported in most promise-based async frameworks in other languages. The lack of support in ES6 is a decision made by ECMA (and, partially, a consequence of the concurrency model in node), not some philosophical impossibility, and there are many valid use cases. – Arkadiy Kukarkin Nov 03 '16 at 20:32
  • In network transmissions order often matters. If you're building a server side application that is sending network traffic, sure you can send the response asynchronously as it's resolved. But it's unacceptable to tell the client on the other end to figure out what order it resolved in to piece together the transmission. So at some point you need to wait for everything to resolve in order to piece together the data you need to send. – Steve Buzonas Jun 09 '17 at 02:05