Ответ Crazy Lazy Cat великолепен и компактен. Но если вам нужны дальнейшие операции и вы не можете использовать такую карту, вам нужно go для решения, похожего на ваш код. Хотя у этих методов есть предостережение, что списки / итерации не должны изменяться из WITHIN во фьючерсы, которые выполняются одновременно / асинхронно. Go до конца моего ответа, чтобы понять это больше.
В любом случае, согласно решению, в этом случае у вас есть две опции:
Опция A.
Перебор списка в пакетном режиме с использованием Future.wait
В этом случае все итерации будут выполняться параллельно (теоретически). Это обеспечивает максимальную производительность / параллелизм.
Все сводится к хранению фьючерсов перед асинхронным их исполнением с использованием Future.wait
.
class Song {
int id;
}
class Playlist {
List<int> songIds;
}
Future<Playlist> getPlaylist(int id) async {
return Playlist();
}
Future<Song> getSong(int songId) async {
return Song();
}
/// Returns list of songs from a `Playlist` using [playlistId]
Future<List<Song>> getSongsFromPlaylist(int playlistId) async {
/// Local function that populates [songList] at [songListIndex] with `Song` object fetched using [songId]
Future<void> __populateSongList(
List<Song> songList,
int songListIndex,
int songId,
) async {
// get the song by its id
Song song = await getSong(songId);
print(song.id);
// add the song to the pre-filled list of songs at the specified index to avoid `ConcurrentModificationError`
songList[songListIndex] = song;
print(
'populating list at index $songListIndex, list state so far: $songList');
} // local function ends here
// get the playlist object by its id
final playlist = await getPlaylist(playlistId);
// create a filled list of pre-defined size to avoid [ConcurrentModificationError](https://api.flutter.dev/flutter/dart-core/ConcurrentModificationError-class.html)
List<Song> songList = List<Song>.filled(playlist.songIds.length, null);
// store futures and execute them in a batch manner using [Future.wait](https://api.dart.dev/stable/2.7.1/dart-async/Future/wait.html)
List<Future<void>> songFutures = [];
for (int listIndex = 0; listIndex < playlist.songIds.length; listIndex++) {
final songId = playlist.songIds[listIndex];
songFutures.add(__populateSongList(songList, listIndex, songId));
}
// execute multiple futures concurrently/in parallel
List<void> songFuturesResult = await Future.wait(songFutures);
/* ALSO VALID
List<void> _ = await Future.wait(songFutures);
await Future.wait(songFutures);
*/
print('returned list: $songList');
return songList;
}
Опция B.
Итерация по list последовательно
В этом случае каждая итерация ожидается, пока не будет выполнена следующая. Производительность не так хороша, как у варианта A, поскольку ожидается, что каждая итерация / вызов останавливает поток управления, следующая итерация / вызов не начнется, пока не закончится предыдущая. Этот метод несколько безопаснее, чем предыдущий, в отношении ConcurrentModificationError
. Для справки только этот блок отличается от предыдущего варианта
// populate songList sequentially -- each iteration/song halted until the previous one finishes execution
for (int listIndex = 0; listIndex < playlist.songIds.length; listIndex++) {
final songId = playlist.songIds[listIndex];
await __populateSongList(songList, listIndex, songId);
}
Но в любом случае вот полное решение:
class Song {
int id;
}
class Playlist {
List<int> songIds;
}
Future<Playlist> getPlaylist(int id) async {
return Playlist();
}
Future<Song> getSong(int songId) async {
return Song();
}
/// Returns list of songs from a `Playlist` using [playlistId]
Future<List<Song>> getSongsFromPlaylist(int playlistId) async {
/// Local function that populates [songList] at [songListIndex] with `Song` object fetched using [songId]
Future<void> __populateSongList(
List<Song> songList,
int songListIndex,
int songId,
) async {
// get the song by its id
Song song = await getSong(songId);
print(song.id);
// add the song to the pre-filled list of songs at the specified index to avoid `ConcurrentModificationError`
songList[songListIndex] = song;
print(
'populating list at index $songListIndex, list state so far: $songList');
} // local function ends here
// get the playlist object by its id
final playlist = await getPlaylist(playlistId);
// create a filled list of pre-defined size to avoid [ConcurrentModificationError](https://api.flutter.dev/flutter/dart-core/ConcurrentModificationError-class.html)
List<Song> songList = List<Song>.filled(playlist.songIds.length, null);
// populate songList sequentially -- each iteration/song halted until the previous one finishes execution
for (int listIndex = 0; listIndex < playlist.songIds.length; listIndex++) {
final songId = playlist.songIds[listIndex];
await __populateSongList(songList, listIndex, songId);
}
print('returned list: $songList');
return songList;
}
Объяснение
Добавление / удаление в доступную коллекцию (массив, карту, ...) должно выполняться вне фьючерсов, в противном случае будет выдана ошибка времени выполнения для одновременной модификации итерируемой во время итерации.
Ссылки
Решения и обсуждения