Difficultés avec future, async, et await

Je rencontre une difficulté depuis quelques jours dans la compréhension et l’application de ces termes.
Voici donc une partie de mon code :

Ceci est ma méthode qui doit récupérer un document dans firecloud et me créer un objet.

  static Future<UserModel> getUserModel(String uid) async {
await _firestoreInstance
    .collection(_collectionName)
    .doc(uid)
    .get()
    .then((DocumentSnapshot documentSnapshot) {
  if (documentSnapshot.exists) {
    var datas = documentSnapshot.data();
    UserModel user = new UserModel.datas(
        uid, datas['email'], datas['firstname'], datas['lastname']);
    return user;
  } else
    return new UserModel(uid);
});
return null;  }

et voici comment je l’appellle :

    await UserHelper.getUserModel(_fireAuth.currentUser.uid)
    .then((value) async {
  if (value != null) {
    print("OK " + value.firstname);
  }
});

le résultat est que je me retrouve avec une erreur indiquant que la valeur récupérée est null:

The getter ‹ firstname › was called on null

Si vous avez une idée, je suis preneur, et un bon site pour expliquer tout ça me va aussi.

Merci bien

Future est le type de fonction qui permet un résultat asynchrone, l’équivalent de Promise dans le javascript.

Ensuite les mots-clés async et await sont des nouveaux raccourcis pour réduire les .then() en cascade dans ton code.

Pour utiliser await il faut obligatoirement mettre async dans la déclaration de ta fonction.

Ainsi quand tu utilises await devant un future comme dans ton exemple :
await _firestoreInstance .collection(_collectionName) .doc(uid) .get()
Tu vas dire à Dart de ne pas continuer l’exécution du code tant que tu n’as pas reçu le résultat du future.

Donc grâce à await tu peux écrire ton code comme ceci :

final _docs = await _firestoreInstance
    .collection(_collectionName)
    .doc(uid)
    .get();

if(_docs.exists){
var datas = _docs.data();
    UserModel user = new UserModel.datas(
        uid, datas['email'], datas['firstname'], datas['lastname']);
    return user;
  } else
    return new UserModel(uid);

La condition if(_docs.exist) ne sera exécutée qu’une fois le future au-dessus complété et donc stocké dans la variable _docs.

Ton code est bon en soit, regarde ce que te donne un .catchError() avec un print à la suite de ton .then().

C’est aussi peut-être lié à une coquille dans l’appel des champs de ta collection.

ça fonctionne du feu de dieu.
Merci beaucoup Stephan pour cette explication très claire.

1 J'aime

Hello,

Bon désolé je pensais comprendre, mais c’est pas ça en fait.
Cette fois j’ai des await et async imbriqués, et le premier fonctionne mais pas le second.

Plus de détails, dans ma base firecloud j’ai une collection et une sous collection: des catégories et des sous catégories.
L’objectif est de tout caser dans une liste d’objets.

pour simplifier j’ai séparé en 2 fonctions :

static Future<void> loadCategories() async {
  _mainCategories.clear();
  var documents = await _firestoreInstance.collection(_collectionName).get();

  documents.docs.forEach((element) async {
    var datas = element.data();
    CategoryModel category = new CategoryModel(element.id, datas['name']);
    category.imageUrl = datas['folder_name'];
    _mainCategories.add(category);
    await CategoryHelper.loadSubCategories(category);
  });
}

cette fonction récupère les document d’une collection, notez que j’appelle la seconde fonction dans la premiére

static Future<void> loadSubCategories(CategoryModel mainCategory) async {
  var documents = await _firestoreInstance
    .collection(_collectionName)
    .doc(mainCategory.uid)
    .collection(_subCollectionName)
    .get();
  documents.docs.forEach((element) {
    var datas = element.data();
    CategoryModel category = new CategoryModel(element.id, datas['name']);
    mainCategory.addSubCategory(category);
  });
}

cette 2nde fonction a pour but de charger les sous catégories dans une liste de la catégorie.

à priori rien d’insurmontable, et pourtant après l’appel de

await CategoryHelper.loadCategories();

je vérifie mes valeurs et je remarque que le nombre de catégories est normal, mais dans chaque catégorie il n’y a aucune sous catégories.
Je remarque aussi que la deuxieme fonction est bien executée, mais après la sortie de CategoryHelper.loadCategories(), et donc un await n’a pas fonctionné.

Merci d’avance pour le coup de main.

C’est un cas particulier que tu as là, dans une boucle comme forEach (ne faire que du sync en code avec), await n’arrêtera pas le code.

Pour cela tu peux utiliser la fonction Future.forEach() à la place, et attendre ensuite que tout soit fini.

Sinon tu as aussi l’autre écriture de la boucle avec for et in qui devrait aussi fonctionner :

for ( var doc in documents.docs) {
// Ici ton code pour chaque élément
}
1 J'aime

Évidemment il faut que je tombe sur un cas particulier.
Merci beaucoup pour ta patience et tes explications.

Tu devrais créer une formation sur Youtube :stuck_out_tongue: