33

First, I have to mention that I already look through many questions in stackoverflow, but many doesn't answer my question. Not to mention many doesn't even have an answer.

How do I achieve the following, making sure functionB() executes after functionA() finishes?


Note: I do not want to convert my async functions to new Promise(resolve=>{...})
because I'll have to convert the someServiceThatMakesHTTPCall() as well, and any other async functions within the call stack, which is a big change.

  function functionThatCannotHaveAsyncKeyword() {
      functionA()
        .then(async function() {
            await functionB();
        })
        .then(function() {
            console.log('last');
        });
  }

  async function functionA() {
      console.log('first');
      await someServiceThatMakesHTTPCall();
  }

  async function functionB() {
      console.log('second');
      await someServiceThatMakesHTTPCall();
  }
zhuhang.jasper
  • 4,120
  • 4
  • 21
  • 29
  • 1
    NO, you haven't to convert your `someServiceThatMakesHTTPCall`, because there is no difference between async function and Promises – Christian Vincenzo Traina Feb 27 '19 at 08:59
  • `async () => true // returns Promise` – noetix Feb 27 '19 at 09:01
  • 2
    I advise you to learn how promises work, because async/await is an extension of them (almost syntactical sugar!). I swear it's easier than you think :) – Christian Vincenzo Traina Feb 27 '19 at 09:01
  • `functionThatCannotHaveAsyncKeyword` if that function is not meant to be **awaited** from something else, it **can** be `async`, because it will return a `Promise` which will be **ignored** by the caller. Besides, `return functionB()` instead of the async-await is just enough for your case. – briosheje Feb 27 '19 at 09:01
  • @CristianTraìna do you have a favorite tutorial for this? I should also post mine after I make some progress – Nathan majicvr.com Jul 21 '22 at 18:05

3 Answers3

45

Your approach using await in an async then callback will work, but it's unnecessarily complex if all you want to do is call the async function and have its result propagate through the chain. But if you are doing other things and want the syntax benefit of async functions, that's fine. I'll come back to that in a moment.

async functions returns promises, so you just return the result of calling your function:

function functionThatCannotHaveAsyncKeyword() {
    functionA()
        .then(function() {
            return functionB(someArgument);
        })
        .then(function() {
            console.log('last');
        }); // <=== Note: You need a `catch` here, or this function needs
            // to return the promise chain to its caller so its caller can
            // handle errors
}

If you want to pass functionA's resolution value into functionB, you can do it even more directly:

functionA()
    .then(functionB)
    // ...

When you return a promise from a then callback, the promise created by the call to then is resolved to the promise you return: it will wait for that other promise to settle, then settle the same way.

Example:

const wait = (duration, ...args) => new Promise(resolve => {
    setTimeout(resolve, duration, ...args);
});

async function functionA() {
    await wait(500);
    return 42;
}

async function functionB() {
    await wait(200);
    return "answer";
}

functionB()
.then(result => {
    console.log(result); // "answer"
    return functionA();
})
.then(result => {
    console.log(result); // 42
})
.catch(error => {
    // ...handle error...
});

Coming back to your approach using an async then callback: That works too, and makes sense when you're doing more stuff:

const wait = (duration, ...args) => new Promise(resolve => {
   setTimeout(resolve, duration, ...args);
});

async function functionA() {
    await wait(500);
    return 42;
}

async function functionB() {
    await wait(200);
    return "answer";
}

functionB()
.then(async (result) => {
    console.log(result); // "answer"
    const v = await functionA();
    if (v < 60) {
        console.log("Waiting 400ms...");
        await wait(400);
        console.log("Done waiting");
    }
    console.log(v);      // 42
})
.catch(error => {
    // ...handle error...
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Before this, I tried .then(async function() { return await functionB(); }) which doesnt work, i'll try your solution and see if it works and get back here – zhuhang.jasper Feb 27 '19 at 09:25
  • @ZhuHang - `.then(async function() { return await functionB(); })` works just fine, you must have had some *other* problem in the code. But `.then(function() { return functionB(); })` does the same thing more directly. – T.J. Crowder Feb 27 '19 at 09:28
  • @t-j-crowder It seems so, I realised I have another problem that makes all my previous attempts seemed to be not working. Let me get back in a moment – zhuhang.jasper Feb 27 '19 at 09:31
  • @t-j-crowder To return promise chain back to its caller, i have to use new Promise => resolve/reject ? – zhuhang.jasper Feb 27 '19 at 09:42
  • @T.J.Crowder how did you even write all this in 5min. – manish kumar Feb 27 '19 at 09:54
  • @manishkumar - I know promises fairly well, and I took typing in High School. ;-) (*One* of the best pieces of advice my mother gave me.) – T.J. Crowder Feb 27 '19 at 10:04
  • @ZhuHang - No, you only need `new Promise` when you don't already have a promise. When you already have a promise, you just return the promise. `then`, `catch`, and `finally` all create promises, so often you return the result of calling them. [More here](https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it). – T.J. Crowder Feb 27 '19 at 10:07
  • @t-j-crowder So like this right? -> return functionA().then(...).then(...); – zhuhang.jasper Feb 28 '19 at 03:41
  • @T.J.Crowder Another question which im not sure should I open new topic. How do I start with an async function in a promise chain? Instead of "functionA().then()", I need something like: "(async function(){...}).then()" – zhuhang.jasper Feb 28 '19 at 03:45
  • @ZhuHang - Yes, like that. Re starting with an `async` function: Remember that an `async` function is just syntax sugar for a function returning a promise. So you'd just use `functionA().then(...).then(...)`. – T.J. Crowder Feb 28 '19 at 08:11
  • @T.J.Crowder I mean leave those functionA & functionB away for a moment. If I want to start with an async function, how is the syntax? `(async function(){...}).then(...).then(...)` – zhuhang.jasper Mar 01 '19 at 14:47
  • @ZhuHang - I don't understand the question. – T.J. Crowder Mar 01 '19 at 15:05
  • @T.J.Crowder From your `async then` callback example, u demonstrated how to use `async` inside a `then()`. My question is how to start the promise chain with an async function before any `then()`. – zhuhang.jasper Mar 04 '19 at 02:09
  • @ZhuHang - I showed you that in the answer; see the first code block. I recommend reading up on `async` functions and promises. An `async` function returns a promise, so if it's your starting point, you're starting with a promise. – T.J. Crowder Mar 04 '19 at 07:26
  • @T.J.Crowder no no. I understand both your examples. I'm asking how to start a promise chain with a "define-on-the-spot-async-function". In your example, u call `functionA()` at beginning of chain, which is a "predefined-async-function". Do u get me? haha – zhuhang.jasper Mar 04 '19 at 08:37
  • @ZhuHang - You could use an IIFE: `(async function() { /*...*/ })().then(/*...*/)...` The only difference between that and your example in a comment above is that I'm *calling* it (the `()` before `.then`). – T.J. Crowder Mar 04 '19 at 08:59
  • @T.J.Crowder I see! I'm missing the `()` – zhuhang.jasper Mar 04 '19 at 09:01
  • @T.J.Crowder I think the async await approach inside `then()` still doesn't work. The reason why your example work is because `.wait()` is using promise-resolve, so it will "wait" with or without the `await` keyword. But if my function is such as: `functionB() { await someAsyncA(); await someAsyncB() await someAsyncC() }` , whereby all these 3 functions do not use promise-resolve, they are not executed sequentially. – zhuhang.jasper Mar 06 '19 at 09:10
  • @ZhuHang - *"...so it will "wait" with or without the await keyword..."* The `setTimeout` will, but code calling `wait` will not. Remember, promises don't make code asynchronous, they *observe* things that are *already* asynchronous. Re your functions: They'll be sequential if they return promises (which they will automatically if they're `async` functions) and you're doing that in an `async` function (otherwise, `await someAsyncA()` will be a syntax error). – T.J. Crowder Mar 06 '19 at 09:12
  • 1
    @T.J.Crowder u know what? Sorry, it actually works. It's some other thing, again. – zhuhang.jasper Mar 06 '19 at 09:20
3

You can use promise inside the first method as

function functionThatCannotHaveAsyncKeyword() {
    return new Promise(async(resolve, reject)=> {
          await functionA();
          await functionB();
          console.log('last');
          resolve();    
      });
  }

  async function functionA() {
      console.log('first');
      await someServiceThatMakesHTTPCall();
  }

  async function functionB() {
      console.log('second');
      await someServiceThatMakesHTTPCall();
  }
Rahul Patil
  • 493
  • 5
  • 14
0

if someServiceThatMakesHTTPCall is async you can avoid all that by doing the following:

function functionThatCannotHaveAsyncKeyword() {
    functionA()
        .then(function() {
            return functionB()
        })
        .then(function() {
            console.log('last');
        });
  }

  function functionA() {
      console.log('first');
      return someServiceThatMakesHTTPCall();
  }

  function functionB() {
      console.log('second');
      return someServiceThatMakesHTTPCall();
  }
luissmg
  • 545
  • 4
  • 9