Асинхронная функция возвращает два разных результата один за другим

#function #flutter #dart #asynchronous #async-await

Вопрос:

Я написал асинхронную функцию Flutter/Dart, которая, на мой взгляд, ведет себя неожиданно. Следующая структура кода:

 static Future<bool> verifySometing() async {
   try {
      await getCloudData().then((snapshot) {
         if (snapshot.exists amp;amp; snapshot.hasData) {
            bool dataValid = await validateData(snapshot.data);
            if (dataValid) {
               print('Data is correct');
               return true;
            }
         }
      });
   } catch (e) {
      print('Error $e');
      return false;
   }
   print('Something went wrong');
   return false;
}
 

Ожидаемым результатом будет то, что функция ожидает облачных данных, затем ожидает проверки и возвращает значение true, если данные действительны. В этом случае консоль покажет следующее, и функция вернет значение true:

 Data is correct
 

На практике происходит то, что консоль показывает следующий вывод, и функция сначала возвращает true, а затем false:

 Data is correct
Something went wrong
 

Это противоречит всему, что я думал знать о функциях в Dart, потому что я всегда предполагал, что после запуска return функция будет выполнена. Есть идеи, как это происходит?

Комментарии:

1. Имейте в виду, что при использовании await вам не нужно использовать then и наоборот. Плюс await останавливает выполнение кода, освобождая поток для другой работы, и продолжается только тогда, когда ожидаемая работа выполнена. В отличие от того then , который не останавливает выполнение приведенного ниже кода.

Ответ №1:

Проблема в этой строке.

 await getCloudData().then((snapshot) {
 

Здесь, вместо того, чтобы просто await отвечать, вы также прикрепили then обратный вызов. Таким образом, на самом деле, что бы вы ни возвращали, это возвращаемое значение callback функции, т. Е.,. (snapshot) {}

Функция обратного вызова, которую вам нужно передать, then берет это return true и передает нам в результате await .

Итак, если бы вы поместили что-то вроде

 var bool = await getCloudData().then((snapshot) { ..... });
 

Тогда это bool было бы равносильно true . Это оно. Нет возврата от вашей основной функции.

Измените его на этот,

 var snapshot = await getCloudData();
if (snapshot.exists amp;amp; snapshot.hasData) {
  bool dataValid = await validateData(snapshot.data);
  if (dataValid) {
    print('Data is correct');
    return true;
  }
}
 

Надеюсь, я смог внятно объяснить.

Ответ №2:

В ваших предположениях есть несколько ошибок.

Во-первых, вы прикрепили букву а then к возвращенному будущему getCloudData() . Это означает , что вы не ждете getCloudData() , а вместо этого возвращаете дополнительное будущее getCloudData().then(...) , и это будущее вернется, когда функция обратного вызова, переданная ему, завершится. (И если первое будущее не выдаст ошибку, будет вызван обратный вызов.)

Во-вторых, функция обратного вызова работает в своей собственной области. Таким образом, этот код делает не то, что вы думаете, что он делает:

 bool dataValid = await validateData(snapshot.data);
if (dataValid) {
  print('Data is correct');
  return true;
}
 

Это возвращение повлияет на функцию обратного вызова, а не на verifySomething функцию.

Учитывая это, порядок работы выглядит следующим образом:

  1. validateSomething Функция await s getCloudData().then(...) .
  2. getCloudData() ему звонят.
  3. getCloudData() возвращает, вызывается обратный then вызов, переданный.
  4. (При условии, что снимок содержит данные) validateData вызывается.
  5. (При условии, что данные успешно проверены) «Данные верны» печатается, и функция обратного вызова возвращается true .
  6. validateSomething получает уведомление о том, что ожидаемое будущее завершено, поэтому выполнение возобновляется.
  7. «Что — то пошло не так» печатается, и validateSomething функция возвращается false .

Вообще говоря, такого рода ошибки часто встречаются при смешивании async/await и then моделировании. Если вы не знаете, что делаете, придерживайтесь либо одного, либо другого, предпочтительно async/await шаблона. Например, рефакторинг вашего кода для устранения вызова then выглядит следующим образом:

 static Future<bool> verifySometing() async {
   try {
      final snapshot = await getCloudData();
      if (snapshot.exists amp;amp; snapshot.hasData) {
        bool dataValid = await validateData(snapshot.data);
        if (dataValid) {
          print('Data is correct');
          return true;
        }
      }
   } catch (e) {
      print('Error $e');
      return false;
   }
   print('Something went wrong');
   return false;
}
 

Теперь, когда нет надоедливого закрытия, с которым нужно иметь дело, return он вернется, validateSomething как и ожидалось, и вам не нужно будет решать такие проблемы, как обратные вызовы и область действия.