1

I'm working in a medium sized node.js application with a series of synchronous functions that call other synchronous functions, etc. For the sake of simplicity let's just say that none of these functions returned Promises before this.

function a(){
  ...
  return c();
}
function b(){
  ...
  return c();
}
function c(){
  ...
  return e();
}
function d(){
  ...
  return e();
}
function e(){
   // Do stuff
}

It's actually much more complex than this and has a full suite of unit/integration tests that call these functions as well.

We now need to function e() to wait for the result of an async function:

function e(){
   const someData = await someAsyncFunction();
   const dataINeedNow = someData.dataINeedNow;
   // Do something with dataINeedNow
}

async function someAsyncFunction(){
  ...
  return await someExternalService();
}

It seems like the general wisdom is that once you start returning Promises you should keep returning Promises. In the example above this would involve making a, b, c, d and e all async, but in reality it would involve ~100 changes in our application.

Is there a way to make a single call to await someExternalService(); in the bowels of a node.js application without a major refactor?

user12462220
  • 11
  • 1
  • 3
  • Changing the nature of a function that everything depends on *is* a major change. You cannot really avoid a major refactoring. However, maybe you can [avoid making the function `e` itself asynchronous](https://stackoverflow.com/a/45448272/1048572), e.g. by changing the arguments passed to it? – Bergi Dec 01 '19 at 00:45
  • Everything has to be async up until the function that actually needs to *wait* for the result before continuing the process. If you don't want that you might want to use callback functions or the actual Promise then functionality instead of `await`. – MadeOfMagicAndWires Dec 01 '19 at 00:49
  • If you were starting from scratch, you would make a, b, c, d and e `async`, but remember that Javascript survived for quite some time after Promises but before AsyncFunctions were introduced. Even if a, b, c, d (as summarized in the question) were to remain non-async functions, they will still return promises. Making them `async`would (a) make `await` available, (b) guarantee that a promise is returned under a greater spectrum circumstances, (c) guarantee that errors throw asynchronously. Without those features, you still get basic async *functionality* from your functions. – Roamer-1888 Dec 01 '19 at 01:21
  • you're function e with await won't work since function e isn't async - asynchrony is like a virus, once it's introduced, it spreads very quickly – Bravo Dec 01 '19 at 01:48
  • So as soon as I integrate with a third party function that is `async` I should make every function in my application hierarchy that uses it `async` as well (potentially 100+ changes) ? If this is the correct answer I can do it, but I'm surprised that there is literally no other way to solve this problem in Javascript. – user12462220 Dec 01 '19 at 02:16
  • It's not clear, did `e()` originally return Promise or not? Are you introducing asynchronism fundamentally, or merely introducing async/await into function(s) that already receive/return promises? – Roamer-1888 Dec 01 '19 at 02:28
  • @Roamer-1888 e() did not return a Promise before. It now relies on the result of a Promise. – user12462220 Dec 01 '19 at 02:35
  • Ah right, unfortunately that's the worse scenario of the two. You need to refactor. 100-ish functions ain't too bad. Make an editiing plan and a test plan - and have plenty of coffee availalble. – Roamer-1888 Dec 01 '19 at 02:54
  • you could also use [events](https://nodejs.org/api/events.html#events_class_eventemitter) and when all is said and done just emit to trigger your thing, `events.on('updateThing', someData => e(someData))` .. `events.emit('updateThing', {...})` – Lawrence Cherone Dec 01 '19 at 03:00
  • @user12462220 But why is it that you suddenly need to integrate a third party functionality in a that deep layer of your application? Maybe there's an underlying architectural issue. – Bergi Dec 01 '19 at 03:04

3 Answers3

1

You should probably rewrite the whole chain to return promises to keep consistency and not make your code very confusing.

But, based on the way you wrote the question if you're really returning the result of each subsequent call you can handle the promise result outside the nest.

Return a promise on function e that will be returned all the way back to your original call

    function e(){
        console.log("did e");
        const prom = new Promise((resolve)=>{
            setTimeout(()=>{
              console.log("resolved async");
              resolve("dataResolve")
            },1000)      
        });
        return prom;
    }

Write Do something with dataINeedNow using the response from a

a().then((resArg)=>{
    f(resArg);
})

See an example here

caiocpricci2
  • 7,714
  • 10
  • 56
  • 88
  • Agreed. I try to encourage other developers to avoid async/await entirely. It attempts to make JavaScript read/perform like something it isn't. Much better to just understand callbacks/Promises and use them correctly. – jmkmay Dec 02 '19 at 01:40
  • Not sure this solves the problem in the sense that if `a` is waiting on `b` which is waiting on `c` etc. which ultimately is waiting on `e` and the processing of the async result, then `a()` will complete before the async result is resolved and processed! See your example with the console messages modified to show when each function actually completes https://codepen.io/abulka/pen/poLaxmw?editors=1111 To truly have each level wait on the next level of call, promise callbacks would be needed at each level - at which point its the same amount of work as converting each function to be async. – abulka Aug 03 '22 at 00:28
0

You can

function e(){
  someAsyncFunction().then((someData)=>{
     const dataINeedNow = someData.dataINeedNow;
     // Do something with dataINeedNow
  });
   
}
Lakshitha Ruwan
  • 949
  • 1
  • 9
  • 12
-1

Just handle the Promise in e() using then/catch.

Your e() looks like:

function e(){
   someAsyncFunction()
   .then(data => {
      const dataINeedNow = data.dataINeedNow;
      // Do something with dataINeedNow;
    })
   .catch(e => {
        // Do something if someExternalService throws rejection
        console.log(e);
    })
}

function someAsyncFunction(){
  ...
  // This works provided someExternalService() returns a Promise.
  return someExternalService();
}

async/await is just syntax sugar for Promise handling. I'm not a fan of it because it gives developers who don't want to learn how to properly handle concurrenty in JavaScript a way to make their applications pseudo-synchronous.

If this looks foreign to you I recommend you review concurrency patterns in JavaScript. Specifically callbacks and Promise chaining.

jmkmay
  • 1,441
  • 11
  • 21