1

There are many questions already asking for how to wait for a promise to resolve. The answer to use await is pretty obvious now. What's not so clear, however, is how to do that without making the calling function async. Is that even possible?

Background:

In a SymbolTable class I have a function getAllSymbols(): Set<Symbol>. In a descendant I want to load these symbols dynamically, so I wrote a method loadAllSymbols(): Promise<Symbol>, which I want to call in the overridden method in my DynamicSymbolTable class:

export class DynamicSymbolTable extends SymbolTable {
    public getAllSymbols(): Set<Symbol> {
        const existing = super.getAllSymbols();
        if (existing.size === 0) {
            return await this.loadAllSymbols();
        }

        return existing;
    }

    protected loadAllSymbols(): Promise<Set<Symbol>> {
        // ... load the content from a remote location
    }
}

However, this code requires to make getAllSymbols to be async and return also a promise. But that changes the signature of the method and it is no longer a valid override of the base class method.

How to implement it so that I can wait for the loaded content without making the calling function (getAllSymbols) async?

Note: How to return values from async functions using async-await from function? does not answer my question, because it doesn't even touch the important part of it (which is how to avoid async).

Mike Lischke
  • 48,925
  • 16
  • 119
  • 181
  • 3
    "Is that even possible?" — no – Quentin Mar 18 '21 at 10:50
  • 2
    If the data it needs is in a promise, it must also return a promise. You don't have to use the `async`/`await` syntax to achieve that, though. – jonrsharpe Mar 18 '21 at 10:51
  • `await` returns the resolved value of the promise. Why can't I just return that value as that? – Mike Lischke Mar 18 '21 at 10:55
  • 4
    @MikeLischke because the value isn't there yet. A promise is just a marker that it *will be available* at some point in the future. You cannot convert asynchronous code to synchronous, though. If you order a pizza, you get a receipt that tells you that you *will have a pizza* at some point in the future. You cannot treat that receipt as the pizza itself, though. When you get your number called you can "resolve" that receipt to a pizza. But what you're describing is trying to eat the receipt. – VLAZ Mar 18 '21 at 10:58
  • Because `async` functions *always* return promises. That's how they work - it's just syntactic sugar, there's no magic blocking. – jonrsharpe Mar 18 '21 at 11:03
  • @MikeLischke Off-topic. I recommend you to spend some time reading this book http://learnyouahaskell.com. At least up to this chapter http://learnyouahaskell.com/a-fistful-of-monads It will be obvious to you how effect makes effectful everything it touches so you can't get rid of a promise the way you want. – Yury Tarabanko Mar 18 '21 at 11:20
  • @jonrsharpe That was one of my attempts to get this done, but it didn't help. – Mike Lischke Mar 18 '21 at 11:23
  • @VLAZ The point is however that the only purpose of `await` is to wait until the value is there. So, once it executed you have the value that was promised in your variable. It is beyond me why I cannot just return this value up the chain. I would expect that this scenario is one of the main tasks someone would do with `await`. – Mike Lischke Mar 18 '21 at 11:25
  • 1
    Nothing's _really_ going to help, you _can't_ get this done. That's what all of the existing questions on promises explain. You can't `await` outside an `async` function, an `async` function always returns a promise, so there's no way to square the circle of going from `loadAllSymbols` returning a promise to `getAllSymbols` returning the value from that promise, whether you use that syntax or just `.then`. Again, it's just syntactic sugar, not magic. – jonrsharpe Mar 18 '21 at 11:27
  • @MikeLischke what does `console.log(1); console.log(Math.max(4, 2)); console.log(2);` do? It prints `1`, then the result of the synchronous function, then `2`. Now, what do you think `console.log(1); console.log(operationThatTakes5MinutesToComplete()); console.log(2);` do? Print 1, then hold up the entire thread for 5 minutes, then print 2? That's not very useful. That's why it's better to just know when `operationThatTakes5MinutesToComplete()` finished *and then* continue without waiting for it. This is what promises help with. – VLAZ Mar 18 '21 at 11:27
  • I knew this argument would come sooner or later. What's useful is up to the application. Sometimes waiting for 5 seconds to get something might indeed be the right approach. No worries, I know how to keep the UI alive. – Mike Lischke Mar 18 '21 at 11:57
  • 1
    *"it doesn't even touch the important part of it (which is how to avoid `async`)"* - I don't know how many different ways I can tell you *you can't*. That's what all of the hundreds of existing Q&As on asynchronous JS are telling you. If you don't like that *syntax* you can use `.then` instead, but you can't somehow get around the fact that you want to unwrap the promise before it can possibly have been resolved. `loadAllSymbols` returns a promise, so `getAllSymbols` either has to return a promise or accept that it won’t have a value to return (the first time it's called, at the *very* least). – jonrsharpe Mar 18 '21 at 12:23
  • The problem here is that there should not be such a thing as a `DynamicSymbolTable` IMO. You were asking the wrong question but your must have an interesting use case. Maybe if you rephrased your question by giving more information about the surrounding architecture and what problem an async `SymbolTable` was supposed to solve, you would get more useful answers/comments. – geoffrey Mar 18 '21 at 12:48
  • @jonrsharpe The fact that you mentioned that there's no way to accomplish what I want has absolutely nothing to do with my note that the close reason for my question is an answer that doesn't actually solve the question I have. It's plain wrong to refer to that other question, which asks about something different than my question. I will not reopen this question only for a single reason: there's not much hope that there's a solution. – Mike Lischke Mar 18 '21 at 13:32
  • The dupe, particularly [this answer](https://stackoverflow.com/a/49938356/3001761), shows the two options you have when a method returns a promise. If you don't like either of those, there's not much any of us can do about it. There's not much value to SO in adding to the pile of [literally 10,000 questions](https://stackoverflow.com/questions/linked/14220321?lq=1) about asynchronous activities in JS - others you could look at include https://stackoverflow.com/q/29440632/3001761, https://stackoverflow.com/q/37533929/3001761, ... If you *do* have something distinctive given that, please [edit]. – jonrsharpe Mar 18 '21 at 13:59
  • 2
    The problem with this pile of questions is that, instead of helping the OP get out of the X Y problem, people stay focussed on Y, mark the question as a duplicate of Y in a matter of minutes and X is never properly addressed. – geoffrey Mar 18 '21 at 23:56
  • @geoffrey the X *is* "how do I work with async values". The dupes solve that. The Y is "I want to magically convert asynchronous code to synchronous one and run things out of sequence". The only well-known dupe not (directly) linked here is [Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference](https://stackoverflow.com/q/23667086) but I doubt it's enough. There evidently is not enough information available to prove that Y is impossible. Don't know about others but I've given up trying to prove it in detail to everyone individually. – VLAZ Mar 22 '21 at 09:16
  • 2
    I disagree. What is expressed is an attempt to solve X by making something that should maybe be agnostic of time asynchronous. The problem is related to design: time taints code. You have a choice: either you make the surface area of async code grow and grow or you treat it as impure code and you lift pure synchronous logic in an async context. Without more information on the surrounding algorithm, we don't know if the design decision to make `SymbolTable` async was the best decision and we can't propose an alternative. This question was handled superficially and carelessly by the community. – geoffrey Mar 22 '21 at 11:05

0 Answers0