-1

I need to run a function that calls secondary function conditionally.

If using async/await I could do:-

resultsA= await functionA(id);
if(resultsA.length>0) {
     resultsB=async functionB(resultsA[0].id);
     resultsC=async functionC(resultsA[0].id);
}

FunctionB and FunctionC can be run synchronously as they are only dependant on the id from the 1st element of the array returned by FunctionA.

I'd like to get away from using async/await and use ,then. What's the best way to code this?

functionA(id)
.then ((resultA) => {
    if(resultA.length > 9) {
        functionBpromise=functionB(resultA[0].id);
        functionCpromise=functionC(resultA[0].id);
        promise.all([functionBPromise, functionCPromise])
        .then((resultBoth) => {
        // etc etc
        })
    }
}

I've tried different approaches and searched for a solution but I just can't figure out how to avoid nesting .then.

  • 2
    `resultsA= async functionA(id);` is not valid syntax. Did you mean `await`? – Bergi Apr 28 '23 at 12:54
  • 6
    "*I'd like to get away from using async/await*" - why? `async`/`await` is shorter, faster, and easier to read. – Bergi Apr 28 '23 at 12:55
  • 3
    "*I just can't figure out how to avoid nesting .then.*" - why would you want to avoid that? If you really want to use `.then()`, you should (and need to) embrace nesting. Just don't forget to `return` the promises. – Bergi Apr 28 '23 at 12:56
  • You said B and C can be run synchronously. You mean they do not return a promise? In that case just call them without await or .then() – M. Oberauer Apr 28 '23 at 15:30
  • Don't get the point why you want to avoid async/await, it's syntax is sorter and simpler. – Ujjawal Kumar Apr 29 '23 at 08:24

1 Answers1

1

I mostly encourage avoiding async-await. The async-await abstraction is an abstraction over an abstraction and disguises nested thens under an imperative looking skin. You can simply do as follows

Promise.resolve(10)
       .then(r => r > 9 ? Promise.all([Promise.resolve(11), Promise.resolve(12)]) : [r])
       .then(([p,q]) => q ? console.log(p, q) : console.log(p));

Edit:

There are some comments I need to address;

Promises provide a clear way to encapsulate asynchronous code and isolate it from synchronous code without creating a nested structure. On the other hand, async-await does create a nested structure similar to calling .then() inside a previous .then(), but hides it. This can be useful when you need to access resolutions from outer scopes (previous then stages). In Promises, you need to explicitly carry previous resolutions to the next then stage if needed, as demonstrated in the example above. Although this may seem like extra work, it is actually a very safe and functional way to chain asynchronous tasks. Such as a simple looking async-await code as;

(async function(){
    let a = await Promise.resolve(10),
        b = await Promise.resolve(2),
        c = await Promise.resolve(4);
    console.log(a+b+c);
})();

when done with Promises would be like;

function x(){
  Promise.resolve(10)
         .then(a => Promise.resolve(2)
                           .then(b => Promise.resolve(4)
                                             .then(c => console.log(a+b+c))));
}

You may say that it looks better with async-await but I believe that's not how you should handle the async code and never with the promises as shown above. But all you do with async-await is this. While it resembles the sync code, it also bears the potential to quickly escalate into a mess if you are using async code alongside sync code.

I'm not saying that you should never use async-await, but if you do, you must first understand how your code would look if implemented with Promises. Many novice coders get lost while using async-await and spread random awaits throughout their code or create unnecessary async functions (often in the form of async callbacks).

Another point to consider is that async functions must return Promises, while normal functions can return Promises or synchronous values. By using Promises in normal functions, you have the option to switch to an async timeline or not by relying on a condition.

Promises are also open to constructing much more sophisticated async workflows by employing their resolve and reject functions (functional programming here..!) outside the Promise constructor. Promises are probably as close as JavaScript can get to a functional programming language. Unfortunately, it seems that Promises have been shelved with the introduction of async-await abstraction.

Having said that, since the introduction of top-level await, I also find myself using await or the for await of loops instead of recursion, etc. However, deeper in the code, I mostly prefer Promises.

At the end of the day, I feel like only at gun point I shall attempt to debug a novice asynchronous code with randomly spread asyncs and awaits. Otherwise it's your decision. Use the one that you find yourself most comfortable to live with.

Redu
  • 25,060
  • 6
  • 56
  • 76
  • 2
    can you elaborate on avoiding async/await? I'd rather avoid the plain Promise in favor of async/await. For instance, in C# is strongly recommended async/await than the Promise-like Task. – Mario Vernari Apr 29 '23 at 08:28
  • Not sure what you mean by "*imperative looking skin*". `.then()` is imperative as well. – Bergi Apr 29 '23 at 13:05
  • It sounds a bit weird your explaination to me. You "prefer Promises", but none wants to get rid of Promises. We all do use Promises! We're talking about the much cleaner and readable async/await than the "pyramid of hell" by using the native way. I'd also add that in 2023 none should rely on raw JavaScript code, but rather leverage TypeScript (or others). That truly helps you in many many aspects. That's my humble opinion, but you should explain better to whom is trying to understand the good practices of coding. – Mario Vernari Apr 30 '23 at 06:44
  • @MarioVernari Quite the opposite. I just wanted to show that if we use `async-await`, under the hood we create a nested promise structure which is by default a promise antipattern. However if we implement the same thing properly with promises (like shown in my answer) we avoid nesting with chained thens at the expense of carrying the previous resolutions to the next then stages. Also promises can be employed more flexibly [as shown here](https://stackoverflow.com/a/70541273/4543207) while `async-await` is a kind of a "limited" version of promises with a nice make up. – Redu Apr 30 '23 at 11:25
  • @Redu at this point it's clear we've two very different viewpoints: you're a bit obsessed against async/await, while I'm a bit obsessed toward readabiliy and reliability of the code. From my perspective, async/await together with TypeScript give a solid-enough code (otherwise won't compile): just think if you forget to call `resolve` somewhere with Promises. Even TypeScript can't help: fully legal, but it'll be a...sailor's Promise! – Mario Vernari May 01 '23 at 05:33