Как провести рефакторинг обработки функциональных ошибок во флаттере с помощью dartz
У меня есть два метода
@override
Future<Option<StorageFailure>> init() async {
final root = await getRootDir();
return root.fold(
(failure) => some(failure),
(dir) {
final images = Directory("$dir/images");
final videos = Directory("$dir/videos");
images.create(); // more error handling here (try|either)
videos.create();
},
);
}
@override
Future<Either<StorageFailure, Directory>> getRootDir() async {
try {
final root = await getApplicationDocumentsDirectory();
return right(Directory("${root.path}/files"));
} catch (e) {
return left(StorageFailure(reason: e.toString()));
}
}
На init
после сворачивания мне нужно больше обрабатывать ошибки, но я не люблю слишком много вкладывать свой код. Также я не знаю, как вернутьFailure
из right
функция.
Как лучше связать эти ценности?
1 ответ
Самое замечательное в Either заключается в том, что вы можете объединить действия в цепочку и позаботиться об обработке ошибок только один раз в конце. Если программе не удается получить корневой каталог, часть создания подкаталогов мультимедиа никогда не выполняется. Таким образом, нет необходимости во вложенной обработке ошибок.
Я сам изучаю функциональное программирование. Так что может быть даже лучшее решение, но вот как я бы это сделал:
// I intentionally added all type annotations for better understanding.
Future<Option<StorageFailure>> init() async {
final Either<StorageFailure, Directory> root = await getRootDir();
final Either<StorageFailure, Either<StorageFailure, Success>> result = await root.traverseFuture(createImagesAndVideosSubfolders);
final Either<StorageFailure, Success> flattenedResult = result.flatMap(id);
return flattenedResult.fold((failure) => some(failure), (success) => none());
}
Future<Either<StorageFailure, Success>> createImagesAndVideosSubfolders(Directory dir) async {
try {
await Directory('${dir.path}/images').create();
await Directory('${dir.path}/videos').create();
return right(Success('success'));
} catch (e) {
return left(StorageFailure(reason: e.toString()));
}
}
- получить корневой каталог
- создать каталоги мультимедиа (если getRootDir не удалось, метод root.traverseFuture просто возвращает существующий StorageFailure)
- Сгладьте вложенные Eithers с помощью flatMap и функции id. Метод traverseFuture - это версия карты для фьючерсов. Если бы существовал эквивалент flatMap и для фьючерсов, результат не был бы заключен в один, и последний шаг для сглаживания результата не понадобился бы.
Вы можете еще больше упростить функцию инициализации, просто вернув Either вместо преобразования ее в Option.
Вот так:
Future<Either<StorageFailure, Success>> init() async {
...
return result.flatMap(id);
}