0

What is the best way to call nested future in Flutter ? Or flutter in general ?

In my app, I have to get the current user (which have its data in final storage = new FlutterSecureStorage();.

From this user, I call an API which is by the user id. Theses two parts works fine independently (quite slow, but that's another story).

On many screen, I have to call asynchronious functions, I do this by using Future.delayed, while my coworker is used to use something like this :

  void initState() {
    super.initState();
    getCurrentUser();
  }

  void getCurrentUser() async {
    user = await UserAuth.getCurrentUser();
  }

this is my nested Futures. I need the current user to be loaded on my initState to load my publications. I have to get the user inside this because it can also be the not current user (this function will be used to retrieve current user but also other users publications).

class _UserPublicationsState extends State<UserPublications> {
  List<Publication> listPublications;
  List<Widget> list = [];
  User user;

  @override
  void initState() {
    super.initState();
    Future.delayed(Duration.zero, () async {
      await UserAuth.getCurrentUser().then((value) {
        setState(() {
          user = value;
          UserWS.getPublicationsPerUsers(user).then((value) {
            setState(() {
              listPublications = value;
            });
          });
        });
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    if (listPublications == null) {
      return FullPageLoading();
    }

    String title = "title";

    return SafeArea(
      child: Scaffold(
        appBar: getSimpleAppBar(context, title),
        body: SingleChildScrollView(
          child: getSavedPublications(),
        ),
      ),
    );
  }
}
morganXap
  • 35
  • 6
  • 1
    1. I don't understand what "call nested future" means. You *await* `Future`s. Waiting for a `Future` might involve internally waiting for intermediate `Future`s, but that's an implementation detail that the waitee needs to be concerned with. 2. It's not clear at all why you're using `Future.delayed`. If it's just to create an `async` function, that's not very useful. 3. You usually should just use `FutureBuilder`. Also see: [What is a Future and how do I use it?](https://stackoverflow.com/q/63017280/) – jamesdlin Aug 18 '21 at 09:55
  • 1) By nested, I understand "future depending on another future result". 2) I'm using `Future.delayed` because that's how I've learned to use future. 3) Can I use 2 FutureBuilder in a raw? – morganXap Aug 18 '21 at 10:03
  • 2. Using `Future.delayed` to deal with an existing `Future` makes no sense. Why not use a `Future.delayed` to use the `Future` returned by `Future.delayed`? Why not have infinite calls to `Future.delayed`? 3. I don't understand what "in a raw" means. – jamesdlin Aug 18 '21 at 10:05
  • 2) I don't understand, can you show me any example ? 3) By in a raw, I mean : first, get the user; second; take that user to call another future method – morganXap Aug 18 '21 at 10:08

2 Answers2

0

I'm not sure what is your approach but you can put your futures in a list inside Future.wait()

like this:

Future.wait([
      Future.delayed(Duration.zero),
      Future.delayed(Duration.zero),
    ]);

if you mean that a future is depending on another future you should use the .then() after the future call

like this:

Future.delayed(Duration.zero).then((value) => Future.delayed(Duration.zero));

I hope this is what you are looking for

Abdulrahman
  • 121
  • 5
0

As I mentioned in comments, waiting for a "nested Future" isn't any different than waiting for any other Future. Suppose you have:

Future<String> fetchDataFromServer() async {
  var firstPart = await fetchFoo();
  var secondPart = await fetchBar();
  return '$firstPart $secondPart';
}

A caller would just need to do, say, print(await fetchDataFromServer());. It doesn't matter that fetchDataFromServer internally waits for some other Futures. fetchDataFromServer returns a Future<String> that indicates when the operation is complete. The caller does not need to know nor care whether that operation involves other asynchronous sub-operations or not.

I'm using Future.delayed because that's how I've learned to use future.

Future.delayed itself returns a Future. If you think you need to use Future.delayed to use a Future, then the logical conclusion would be:

var future = fetchDataFromServer();
var future2 = Future.delayed(Duration.zero, () async => await future);
var future3 = Future.delayed(Duration.zero, () async => await future2);
var future4 = Future.delayed(Duration.zero, () async => await future3);
// Ad infinitum.

Typically you'd use Future.delayed to actually introduce a delay, often to treat an a synchronous operation as an asynchronous one for testing or for demonstrative purposes. If you have an existing Future already, then wrapping it with Future.delayed(Duration.zero, () => await existingFuture) is pointless. Just use existingFuture directly.

Finally, using await is syntactic sugar for setting up a .then() callback. Mixing both await and .then() is inconsistent at best and confusing at worst.

await someFuture.then((value) => doSomething(value));

is equivalent to:

var value = await someFuture;
doSomething(value);

Putting the above together, your code:

@override
void initState() {
  super.initState();
  Future.delayed(Duration.zero, () async {
    await UserAuth.getCurrentUser().then((value) {
      setState(() {
        user = value;
        UserWS.getPublicationsPerUsers(user).then((value) {
          setState(() {
            listPublications = value;
          });
        });
      });
    });
  });
}

is equivalent to:

Future<void> _updatePublications() async {
  var currentUser = await UserAuth.getCurrentUser();
  setState(() {
    user = currentUser;
  });

  var publications = await UserWS.getPublicationsPerUsers(user);
  setState(() {
    listPublications = publications;
  });
}

@override
void initState() {
  super.initState();

  _updatePublications();
}

Note that with the simplified version, it's more apparent that you might want to combine the two setState calls so that you rebuild your widget only after user and listPublications have both been updated.

jamesdlin
  • 81,374
  • 13
  • 159
  • 204