Crazy Lazy Cat's answer 很棒而且很紧凑。但是,如果您需要进一步的操作并且不能使用这样的地图,那么您必须寻求类似于您的代码的解决方案。尽管这些方法有一个警告,即不应从并发/异步执行的期货中修改列表/可迭代对象。转到我的答案的末尾以进一步了解这一点。
无论如何,根据解决方案,在这种情况下,您有两个选择:
选项 A。
在这种情况下,所有迭代都将并行执行(理论上)。这确保了最佳性能/并发性。
归结为在使用 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。
按顺序遍历列表
在这种情况下,每次迭代都会等待,直到执行下一次。性能不如选项 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;
}
说明
访问的集合(数组,映射,...)的添加/删除必须在futures之外完成,否则在迭代期间同时修改一个iterable会引发运行时错误。
参考文献
解决方案和讨论