#list #flutter #dart
#Список #flutter #dart
Вопрос:
Во время тестирования и отладки приложения я заметил, что было исключение, которое в основном происходит только во время отладочного тестирования, внутри цикла for, который перебирает список:
[ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Concurrent modification during iteration: Instance(length:0) of '_GrowableList'.
Я искал и обнаружил, что в основном это происходит, если вы меняете сам список во время итерации, но я не вижу, где это происходит в коде:
Основная функция:
static Future<void> save(EntryModel entry) async {
...
List<TagModel> tagsList = entry.tags;
List<int> tagIdsInserted = [];
if (tagsList != null amp;amp; tagsList.isNotEmpty) {
for (TagModel tag in tagsList) {
//Error happens inside this loop
int tagIdInserted = await TagContract.save(tag); //this function does not alter the tag in any way.
if (tagIdInserted == null || tagIdInserted <= 0) {
throw Exception('Invalid TagID!');
}
tagIdsInserted.add(tagIdInserted);
}
}
Что происходит, так это то, что во время первой итерации она работает нормально, но вторая или третья List<TagModel> tagsList
внезапно становится пустой, в том числе из исходного объекта ( entry
переданного функции).
Также я заметил, что во время выполнения без отладки он работает в основном нормально, но я не уверен, связано ли это с тем, что я не улавливаю ошибку.
Заранее спасибо.
Комментарии:
1. Содержит ли TagModel ссылку на свою EntryModel ? Что делает TagContract при сохранении? Существует ли какой-либо менеджер моделей, который может очищать теги EntryModel? Недостаточно информации для устранения проблемы. Вам придется использовать отладчик для пошагового выполнения кода.
2. @hola Нет, TagModel не содержит никаких ссылок на запись. TagContract записывает информацию о TagModel в базе данных SQLite. Я попытался отладить код и заметил, что на второй или третьей итерации цикла EntryModel по какой-то причине очищает свою ссылку на список TagModel. У меня до сих пор нет идеи, что может быть причиной проблемы, но ответ First_Strike, похоже, проясняет некоторые проблемы.
Ответ №1:
Старайтесь избегать использования await
внутри цикла, это слишком опасно.
Вы должны понимать, как выполняется асинхронный код. Если await
обнаружен и Future
не может вернуться синхронно, среда выполнения приостановит выполнение этой функции и перейдет к любым другим заданиям, которые находятся в верхней части очереди.
Поэтому, когда await
возникает проблема, среда выполнения начнет выполнять какой-то бог знает какой код, и этот код коснулся вашего tagsList
.
Попытайтесь понять следующий пример. Это напрямую вызовет исключение.
void main() {
List<int> ids = [1,2,3];
test(ids);
ids.add(1); // If the async function get suspended, this becomes the top of the queue.
}
void test(List<int> ids) async {
for (final id in ids) {
await Future.delayed(Duration(milliseconds: 10));
}
}
В асинхронном программировании избегайте написания await
who зависит от открытых общих состояний.
Для списка асинхронных задач всегда подготавливайте их в an Iterable<Future>
, затем используйте Future.wait
для их синхронизации и получения результата в одном await
.
Для вашего кода
final results = await Future.wait(tagsList.map((tag)=>TagContract.save(tag)))
Комментарии:
1. Я протестировал код с вашим предложением. Пока это, похоже, работает, но я все еще не уверен, было ли это причиной проблемы. Я буду искать в документации, как работают фьючерсы, но, тем не менее, спасибо за совет.
2. Dart
Future
работает так же, как и JavascriptPromise
. Вы можете обратиться к обеим документациям. Лучше подготовить и использовать локальное состояние перед anawait
, потомуawait
что означает «здесь планировщик времени выполнения может выполнять произвольные другие задачи, чтобы ждать»