98 votes

Dart, comment créer un futur à retourner dans vos propres fonctions ?

Le système de gestion de l'information de Dart est un système de gestion de l'information qui permet à l'utilisateur d'avoir accès à l'ensemble des informations relatives à la gestion de l'information de Dart.

Je souhaite définir une fonction qui renvoie toujours un Future<List<Base>> qu'il s'agisse d'un appel asynchrone (lecture de fichier/ajax/etc) ou de l'obtention d'une variable locale, comme ci-dessous :

List<Base> aListOfItems = ...;

Future<List<Base>> GetItemList(){

    return new Future(aListOfItems);

}

157voto

Fox32 Points 5527

Si vous devez créer un futur, vous pouvez utiliser un fichier Completer . Voir Completer classe dans les documents. Voici un exemple :

Future<List<Base>> GetItemList(){
  var completer = new Completer<List<Base>>();

  // At some time you need to complete the future:
  completer.complete(new List<Base>());

  return completer.future;
}

Mais la plupart du temps, il n'est pas nécessaire de créer un avenir avec un compléteur. Comme dans ce cas :

Future<List<Base>> GetItemList(){
  var completer = new Completer();

  aFuture.then((a) {
    // At some time you need to complete the future:
    completer.complete(a);
  });

  return completer.future;
}

Le code peut devenir très compliqué en utilisant des compléteurs. Vous pouvez simplement utiliser ce qui suit à la place, car then() renvoie un Future également :

Future<List<Base>> GetItemList(){
  return aFuture.then((a) {
    // Do something..
  });
}

Ou un exemple de fichier io :

Future<List<String>> readCommaSeperatedList(file){
  return file.readAsString().then((text) => text.split(','));
}

Voir cet article de blog pour plus de conseils.

88voto

Sebastian Engel Points 1250

Vous pouvez simplement utiliser la fonction Future<T>value le constructeur de l'usine :

return Future<String>.value('Back to the future!');

51voto

Suragch Points 197

Retourner un futur à partir de sa propre fonction

Cette réponse est un résumé des nombreuses façons de procéder.

Point de départ

Votre méthode peut être n'importe quoi, mais pour les besoins de ces exemples, disons que votre méthode est la suivante :

int cubed(int a) {
  return a * a * a;
}

Actuellement, vous pouvez utiliser votre méthode comme suit :

int myCubedInt = cubed(3);  // 27

Cependant, vous souhaitez que votre méthode renvoie un Future comme ceci :

Future<int> myFutureCubedInt = cubed(3);

Ou de pouvoir l'utiliser de manière plus pratique comme ceci :

int myCubedInt = await cubed(3);

Les solutions suivantes proposent toutes des moyens d'y parvenir.

Solution 1 : Constructeur Future()

La solution la plus simple consiste à utiliser le constructeur génératif de Future .

Future<int> cubed(int a) {
  return Future(() => a * a * a);
}

J'ai modifié le type de retour de la méthode en Future<int> et a ensuite transmis le travail de l'ancienne fonction en tant que fonction anonyme à la fonction Future constructeur.

Solution 2 : Constructeur nommé futur

Les contrats à terme peuvent se terminer par une valeur ou une erreur. Ainsi, si vous souhaitez spécifier explicitement l'une ou l'autre de ces options, vous pouvez utiliser l'option Future.value o Future.error des constructeurs nommés.

Future<int> cubed(int a) {
  if (a < 0) {
    return Future.error(ArgumentError("'a' must be positive."));
  }
  return Future.value(a * a * a);
}

Ne pas autoriser une valeur négative pour a est un exemple inventé pour montrer l'utilisation de la fonction Future.error constructeur. S'il n'y a rien qui puisse produire une erreur, vous pouvez simplement utiliser la fonction Future.value comme suit :

Future<int> cubed(int a) {
  return Future.value(a * a * a);
}

Solution 3 : méthode asynchrone

Un async renvoie automatiquement un Future vous pouvez donc simplement marquer la méthode async et modifiez le type de retour comme suit :

Future<int> cubed(int a) async {
  return a * a * a;
}

Normalement, vous utilisez async en combinaison avec await mais rien ne dit que vous devez le faire. Dart convertit automatiquement la valeur de retour en une valeur Future .

Dans le cas où vous utilisez une autre API qui renvoie une valeur de Future dans le corps de votre fonction, vous pouvez utiliser await comme suit :

Future<int> cubed(int a) async {
  return await cubedOnRemoteServer(a);
}

Ou bien c'est la même chose en utilisant l'option Future.then syntaxe :

Future<int> cubed(int a) async {
  return cubedOnRemoteServer(a).then((result) => result);
}

Solution 4 : Compléteur

L'utilisation d'un Completer est la solution la plus simple. Elle n'est nécessaire que si vous avez une logique complexe que les solutions ci-dessus ne couvrent pas.

import 'dart:async';

Future<int> cubed(int a) async {
  final completer = Completer();
  if (a < 0) {
    completer.completeError(ArgumentError("'a' must be positive."));
  } else {
    completer.complete(a * a * a);
  }
  return completer.future;
}

Cet exemple est similaire à la solution du constructeur nommé ci-dessus. Il gère les erreurs en complétant le futur de la manière habituelle.

Remarque sur le blocage de l'interface utilisateur

Rien dans l'utilisation d'un futur ne garantit que vous ne bloquerez pas l'interface utilisateur (c'est-à-dire l'isolat principal). Renvoyer un futur à partir de votre fonction indique simplement à Dart de planifier la tâche à la fin de l'isolat principal. file d'attente d'événements . Si cette tâche est intensive, elle bloquera toujours l'interface utilisateur lorsque la boucle d'événements en programmera l'exécution.

Si vous avez une tâche intensive que vous souhaitez exécuter sur un autre isolat, vous devez créer un nouvel isolat pour l'exécuter. Lorsque la tâche se termine sur l'autre isolat, elle renvoie un message en tant que futur, que vous pouvez transmettre en tant que résultat de votre fonction.

La plupart des classes d'IO standard de Dart (comme File o HttpClient ) ont des méthodes qui délèguent le travail au système et n'effectuent donc pas leur travail intensif sur le fil d'exécution de l'interface utilisateur. Ainsi, les futurs renvoyés par ces méthodes ne risquent pas de bloquer votre interface utilisateur.

Voir aussi

7voto

sector11 Points 1

@Fox32 a la bonne réponse. De plus, nous devons mentionner le type du compléteur, sinon nous obtenons une exception.

Exception received is type 'Future<dynamic>' is not a subtype of type 'FutureOr<List<Base>>

Ainsi, l'initialisation du compléteur deviendrait

var completer= new Completer<List<Base>>();

1voto

Anton Duzenko Points 35

Ce n'est pas exactement la réponse à la question posée, mais il arrive que l'on veuille await une fermeture :

    flagImage ??= await () async {
      ...
      final image = (await codec.getNextFrame()).image;
      return image;
    }();

Je pense qu'il crée implicitement un avenir, même si nous ne le transmettons nulle part.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X