前提: await と async
API通信などで非同期処理を実現する上で使用します。
- async: 非同期メソッドを定義する場合に使用する
- await: 非同期メソッドの実行を待ってから以降の処理をしたい場合に使用する
参考記事
下記にわかりやすく記載してくれてます
https://qiita.com/tanaka512/items/6a326b0d92667b3cac65
やりたいこと
- 2つのAPIからデータを取得する
final List<String> _requestSoundsUrls = [
'assets/json/soundBox.json',
'assets/json/audioList.json'
];
- 下記でAPIへリクエストし、UIの描画に使用するJson形式のデータを取得
await SoundsApiProvider().fetchSoundsApi(_requestSoundsUrls[i]);
- レスポンスのJsonをパースし、soundsListに格納
List<Map<String, dynamic>> jsonAudioList =
new List<Map<String, dynamic>>.from(jsonArray['audioList']);
List<Sounds> soundsList =
jsonAudioList.map((i) => new Sounds.fromJson(i)).toList();
soundsLists.add(soundsList)
問題のコード1 :List.forEach (非同期処理がうまく行かない)
このコード(error.dart)だとawaitしている下記のコードを待たずにコードが流れてしまい、UIでNULLエラーが返却される
await SoundsApiProvider().fetchSoundsApi(_requestSoundsUrls[i]);
error.dart
final List<String> _requestSoundsUrls = [
'assets/json/soundBox.json',
'assets/json/audioList.json'
];
List<List<Sounds>> soundsLists = List<List<Sounds>>();
Future.forEach(_requestSoundsUrls, (url) async {
final String _soundsResponse =
await SoundsApiProvider().fetchSoundsApi(url);
if (_soundsResponse != "") {
Map jsonArray = json.decode(_soundsResponse);
// http通信ではなくて、ローカルからファイル取得
List<Map<String, dynamic>> jsonAudioList =
new List<Map<String, dynamic>>.from(jsonArray['audioList']);
List<Sounds> soundsList =
jsonAudioList.map((i) => new Sounds.fromJson(i)).toList();
soundsLists.add(soundsList);
} else {
throw Exception("ERROR");
}
});
問題のコード2 :Future.forEach (非同期処理がうまく行かない)
こちらも結果は同じ
error.dart
final List<String> _requestSoundsUrls = [
'assets/json/soundBox.json',
'assets/json/audioList.json'
];
List<List<Sounds>> soundsLists = List<List<Sounds>>();
_requestSoundsUrls.forEach((url) async {
final String _soundsResponse =
await SoundsApiProvider().fetchSoundsApi(url);
if (_soundsResponse != "") {
Map jsonArray = json.decode(_soundsResponse);
// http通信ではなくて、ローカルからファイル取得
List<Map<String, dynamic>> jsonAudioList =
new List<Map<String, dynamic>>.from(jsonArray['audioList']);
List<Sounds> soundsList =
jsonAudioList.map((i) => new Sounds.fromJson(i)).toList();
soundsLists.add(soundsList);
} else {
throw Exception("ERROR");
}
});
発生したエラー
RangeError (index): Invalid value: Valid value range is empty: 0
対処法
対処法1: await Future.ForEach (うまくawaitされる)
Futureを返す、非同期メソッドはawaitが使える。
awaitをつければ、中の処理が終わるのを待ってくれる (優秀な先輩に教えてもらいました。ありがとうございます!)
ちなみに、await List.ForEach は文法エラーになる。(⚠注意)
success1.dart
final List<String> _requestSoundsUrls = [
'assets/json/soundBox.json',
'assets/json/audioList.json'
];
List<List<Sounds>> soundsLists = List<List<Sounds>>();
_requestSoundsUrls.forEach((url) async {
final String _soundsResponse =
await SoundsApiProvider().fetchSoundsApi(url);
if (_soundsResponse != "") {
Map jsonArray = json.decode(_soundsResponse);
// http通信ではなくて、ローカルからファイル取得
List<Map<String, dynamic>> jsonAudioList =
new List<Map<String, dynamic>>.from(jsonArray['audioList']);
List<Sounds> soundsList =
jsonAudioList.map((i) => new Sounds.fromJson(i)).toList();
soundsLists.add(soundsList);
} else {
throw Exception("ERROR");
}
});
対処法2: for (うまくawaitされる)
単純なforで再起させたこのコード(success.dart)だと、ちゃんとawaitを待ってくれるようになり、NULLエラーが解消された
success2.dart
final List<String> _requestSoundsUrls = [
'assets/json/soundBox.json',
'assets/json/audioList.json'
];
List<List<Sounds>> soundsLists = List<List<Sounds>>();
for(int i = 0; i < _requestSoundsUrls.length; i++){
final String _soundsResponse =
await SoundsApiProvider().fetchSoundsApi(_requestSoundsUrls[i]);
if (_soundsResponse != "") {
Map jsonArray = json.decode(_soundsResponse);
// http通信ではなくて、ローカルからファイル取得
List<Map<String, dynamic>> jsonAudioList =
new List<Map<String, dynamic>>.from(jsonArray['audioList']);
List<Sounds> soundsList =
jsonAudioList.map((i) => new Sounds.fromJson(i)).toList();
soundsLists.add(soundsList);
} else {
throw Exception("ERROR");
}
}