Battement. Asynchronie (async) <> parallélisme (isoler). Du tout

introduction



Récemment, j'ai été surpris de constater que mes collègues ne savaient pas complètement ce qu'est l'asynchronie dans Flutter. Pour une raison quelconque, ils ont eu l'idée que si la fonction asynchrone est correctement écrite, elle ne bloque pas l'interface. Après avoir fait défiler quelques articles, je n'ai pas trouvé d'explication simple, complète et claire de toute cette cuisine (tout est selon le principe - «choisissez 2 sur 3»)). Dans un article, j'ai même lu que Dart avait une merveilleuse asynchronie, ce qui vous permet de reporter l'exécution du code jusqu'à ce que le fil soit plus libre (ce qui à mon avis est un peu trompeur) (Remarque: dans les commentaires nikita_dola souligné ce que signifiait probablement - scheduleTask ).



Pour qui l'article



L'article est destiné à ceux qui commencent tout juste à se familiariser avec Flutter, donc je vais essayer de montrer avec un exemple simple dans cette petite note que l'asynchronie est juste la capacité d'exécuter du code non séquentiellement. Mais, si vous avez une fonction "lourde" (même si elle est trois fois asynchrone), elle bloquera toujours l'interface pour vous. Bien sûr, dans un produit réel, il est peu probable que vous rencontriez des manifestations aussi évidentes (pour le moment, les processeurs sont assez puissants), mais cela vaut toujours la peine de comprendre comment cela fonctionne.



Aller



Et donc, prenons un exemple de la documentation de la bibliothèque flutter_bloc pour les expériences . Modifions légèrement la fonction "_mapTimerStartedToState" de la classe timer_bloc - commentons la mise à jour du compteur pour qu'elle n'interfère pas:



Stream<TimerState> _mapTimerStartedToState(TimerStarted start) async* {
  yield TimerRunInProgress(start.duration);
  _tickerSubscription?.cancel();
  // _tickerSubscription = _ticker
  //     .tick(ticks: start.duration)
  //     .listen((duration) => add(TimerTicked(duration: duration)));
}

      
      







Ajoutons une nouvelle fonction statique (nous la faisons comme ça à l'avance - isolate ne fonctionne qu'avec eux) fonction:



static Future<void> _heavyComput (SendPort sendPort) async {
  await Future.delayed(Duration(seconds: 5));

  print('=======================');
  print('!!!function finished!!!');
  print('=======================');
  return null;
}

      
      







Ici, comme une émulation de calculs lourds, nous attendons la fin du délai de 5 secondes.

Nous modifions la fonction mapEventToState - ajoutons un appel asynchrone à _heavyComput à la fin:



@override
Stream<TimerState> mapEventToState(
  TimerEvent event,
) async* {

. . .
  
  _heavyComput(null);
}

      
      







Pour le premier test, tout est prêt - notre tâche est d'observer les vagues magiques.

Nous lançons et voyons - les ondes sont inquiètes, l'interface n'est pas bloquée, le message concernant la fin de la fonction s'affiche après 5 secondes.







C'est une merveilleuse asynchronie - la panique était fausse. Hmm ... Et si Future.delayed (Durée (secondes: 5)) est remplacé par une boucle?



static Future<void> _heavyComput(SendPort sendPort) async {
  int pause = 1200000000;
  for (int i = 0; i < pause; i++) {}
  print('=======================');
  print('!!!function finished!!!');
  print('=======================');
  return null;
}

      
      





On le lance et c'est tout - "arrivé" - les vagues ne s'inquiètent plus.







Je ne pense pas que beaucoup d'explications soient nécessaires ici: même une fonction lourde asynchrone bloque tout. Par défaut, tout le code est exécuté dans un thread. C'est juste que dans le premier cas, aucun calcul n'était nécessaire, mais il fallait juste attendre, et dans le second, des calculs étaient nécessaires.



Eh bien, et pour que l'article ne se révèle pas complètement microscopique, appelons cette fonction en utilisant isolat. Changeons le mapEventToState:



@override
Stream<TimerState> mapEventToState(
  TimerEvent event,
) async* {
. . .
  var _receivePort = ReceivePort();
  var _isolate = Isolate.spawn(_heavyComput, _receivePort.sendPort);
}

      
      







Nous le démarrons et voyons que l'interface n'est pas bloquée, nous recevons un message sur l'achèvement de la fonction avec un retard notable.







C'est tout (comment async et wait fonctionnent - il y a beaucoup d'articles, je pense que vous ne devriez pas vous arrêter là).



Un exemple peut être téléchargé à partir du lien - flutter_timer_async_and_parallels



All Articles