0

Trying to get my head around the subtle (sic) differences between await vs then. I've read most of the posts here, so I get that async functions return a promise.

Using await (with an async function) returns a var that can be used downstream, but using .then (with an async function) does not (at least for me) likewise return a var that can be accessed outside the then clause), since it is processed asynchronously and downstream references are processed synchronously - outside the .then clause, the var (in my example) is undefined.

I understand why my example is behaving the way it does, but my question is - is there a way to use .then with an async function, such that the result can be accessed downstream of the function execution?

let x, y, z;

async function foo ( num ) {
    return Promise.resolve ( num + 10 );
}

async function bar (num) {
    return num + 20;
}

async function baz (num) {
    return Promise.resolve ( num + 30 );
}

async function main (){
    x = await foo(10);
    console.log('foo returned (after await): '+ x); // no hay problema!

    y = await bar (10);
    console.log('bar returned (after await): '+ y); // no hay problema!

    baz (10)
        .then ( (result) =>{
            z = result;
        });
    console.log('baz returned: '+ z); // undefined...executes before .then completes...
}

main(); 

Update

I guess there were some questions about my objective in using x and y outside a set of chained then clauses. Here is a (hopefully more) real-world example of what I'm trying to do:

In the code below, schemaValidation can be used in any of the routes within an express app (said routes are initialized in initAppRouter):

// main
    const schemaDB = await initAppDBSchema ();
    const schemaValidation = await initAppDataValidationSchema();
    const Author = mongoose.model ( 'Author', schemaDB );
    let author: any;
    let authors: any;
    await initAppDB ();
    await initAppRouter ();

    async function initAppDataValidationSchema () {
        return joi.object ( {
            authorName: joi.string ()
                .min ( 3 )
                .max ( 30 )
                .required (),
etc...
        } );
    }  ...
IMTanuki
  • 117
  • 1
  • 9

3 Answers3

1

is there a way to use .then with an async function, such that the result can be accessed downstream of the function execution?

No. There's no reliable way to do that.

Moreover, you shouldn't need to do that in the first place. Whatever you want to do with the data after the promise chain can be done inside the callback function, once the data is available.

Using await (with an async function) returns a var that can be used downstream

Right but that's only possible because of how async-await is transformed in the promise chain. That "downstream" code is actually wrapped in a callback function of then() method.

Yousaf
  • 27,861
  • 6
  • 44
  • 69
  • Thx for the super-quick reply. However,re " you shouldn't need to do that in the first place" - what happens when the async function is fetching data that will be used throughout the app? If I understand your comment, that suggests a _lot_ of code needs to be embedded within the callback... – IMTanuki Oct 13 '21 at 14:34
  • In that case, you probably have central store of state; you can update that central state. In your code, i would just use `async-await` syntax. There's no need to use `then` method – Yousaf Oct 13 '21 at 14:35
  • With the caveat that I'm relatively new to js, I'm not sure I understand / agree with the statement that "the downstream code is actually wrapped in a callback function". In my example, when x is updated, I believe any other code segment can access x, irrespective of the original implicit .then clause...is that correct? – IMTanuki Oct 13 '21 at 14:39
  • Yeah, generally I've been using await and it works fine. I glommed on to this topic, because I (erroneously) did not realize that an async function returned a promise (hence the return Promise.resolve(xxx) in my example to demonstrate (to myself) how async functions return a promise). – IMTanuki Oct 13 '21 at 14:43
  • `async-await` syntax is just syntactic sugar over regular promise chain: `p.then().then().catch()`. `async-await` syntax is transformed into a regular promise chain and that _"downstream"_ code is actually inside the callback function of `then` method. That is why, after the `await`, you can access the variable `x`. – Yousaf Oct 13 '21 at 14:44
  • You should probably read: [How to return the response from an asynchronous call](https://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call) – Yousaf Oct 13 '21 at 14:48
  • thx. I've read that link.... – IMTanuki Oct 13 '21 at 15:01
0

there is no way to make a promise behave synchronously like you desire. The only way is to move your code dependent of the promise inside the then block and continue there.

baz (10)
        .then ( (result) =>{
            z = result;
            console.log('baz returned: '+ z); // undefined...executes before .then completes...
        });
c0l3
  • 765
  • 6
  • 14
  • Thx! Maybe I'm missing sth here, but it seems to me that the evolutionary nature of js (from callbacks to promises to async/await) resulted in a somewhat limited design - if I have to put everything that is dependent on the promise inside a series of chained .then statements, then I'm kind of stuck with a version of callback hell...this is off-topic, but the general problem I'm running into is that, when I have functionality that is dependent on a data fetch, I've got (deeply nested) async functions for those downstream featrues. sorry if that doesn't make sense - realtively new to js... – IMTanuki Oct 13 '21 at 14:51
  • thx everyone for the great answers! – IMTanuki Oct 13 '21 at 15:12
  • you are missing that promises can be [chained](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#chaining) and [compsitioned](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#chaining). Which will not get you into callback hell. – c0l3 Oct 14 '21 at 13:33
0

You have to either wait for every promise to resolve with await so code will behave in non async manner, or chain callback with then

await baz (10)
        .then ( (result) =>{
            z = result;
        });

    console.log('baz returned: '+ z)

so everything is done, or

baz (10)
        .then ( (result) =>{z = result;})
        .then ( () =>{console.log('baz returned: '+ z)})
Antoniossss
  • 31,590
  • 6
  • 57
  • 99
  • Understood on your second example - but what I'm trying to do is decouple the updating of a var with its subsequent usage... – IMTanuki Oct 13 '21 at 14:53
  • Then letter example does that. – Antoniossss Oct 13 '21 at 14:54
  • Its the nature of async code. Can you give me example of what would your expectations be (in different language ofc) regarding async code? To me it is clear and convinient yo use either await or callback chaining. Especially since I work some time with rxjs which main class os `Observable` which behaves similarly to Promises and uses pipes to chain the callbacks (so its similar to Promise#then) – Antoniossss Oct 13 '21 at 14:56
  • You will switch .then chain into .pipe(array of callbacks). And to be honest I still dont understand your problem. You want to see the result of some operation that will happen (or not) somewhere in the future and see that result without waiting -by any means- for that operation to complete. This makes no sense. Its like you would like to be cook a diner without the ingridients only because you will visit groceriey in the future and buy that ingridients. it must be Promise.resolve(buy ingridients).then(cook dinner) You have straight causality there, but yet you want to erase it. – Antoniossss Oct 14 '21 at 06:43
  • Yes it does (make no sense) as you clearly shows the causality and yet you expect it to be gone somehow by seeing the effect before the cause. I think you have misunderstood how actually your code works and why. This is actually what is stated in the answer you have accepted. You want to justify bad code design / solution approach by requesting missing magic into the language. As written "Moreover, you shouldn't need to do that in the first place." - and I agree. – Antoniossss Oct 15 '21 at 06:05
  • Stay on topic, dont diverge onto my personal time. I am sorry to hear that my explanation is not helpfull. I think it explains in excelence flaw in your expectations. Maybe this is the problem. Cheers! – Antoniossss Oct 15 '21 at 18:02
  • Its true - I have stated that - I have no idea what is your problem as everything is clear (you have invented the problem by misunderstanding async-await nature) – Antoniossss Oct 15 '21 at 20:17
  • If you actually took the time to read my post, you'd know that I stated that async / await does exactly what I need it to. – IMTanuki Oct 15 '21 at 21:15