Давайте продолжим говорить о Вяз 0.18 .
Вяз.
Удобно и неудобно Вяз.
Удобно и неловко.
Состав Вяз.
Удобно и неловко.
Json.Encoder и Json.Decoder В этой статье мы рассмотрим вопросы взаимодействия с серверной частью.
Выполнение запросов
Примеры простых запросов можно найти в описании пакета.HTTP .
Тип запроса - Http.Запросить .
Тип результата запроса — Результат HTTP.Ошибка а.
Оба типа параметризуются определяемым пользователем типом, декодер которого необходимо указать при формировании запроса.
Выполнить запрос можно с помощью функций:
- HTTP.отправить;
- http.toTask.
Сообщение несет данные о результате запроса.
Http.toTask позволяет вам создать Задача , что можно сделать.
Использование функции Http.toTask, на мой взгляд, наиболее удобно, поскольку экземпляры Task можно объединять друг с другом при используя различные функции , Например Задача.
map2 .
Давайте посмотрим на пример.
Допустим, для сохранения пользовательских данных необходимо выполнить два последовательных зависимых запроса.
Пусть это будет создание поста от пользователя и сохранение к нему фотографий (используется определенный CDN).
Сначала давайте посмотрим на реализацию случая Http.Send. Для этого нам понадобятся две функции:
Типы UserData и CDNData описываться не будут; они не важны для примера.save : UserData -> Request Http.Error UserData save userData = Http.post “/some/url” (Http.jsonBody (encodeUserData userData)) decodeUserData saveImages : Int -> Images -> Request Http.Error CDNData saveImages id images = Http.post (“/some/cdn/for/” ++ (toString id)) (imagesBody images) decodedCDNData
Функция encodeUserData является кодировщиком.
saveImages принимает идентификатор пользовательских данных, который используется для генерации адреса, и список фотографий.
Функция imagesBody генерирует тело запроса типа multipart/форма-данные .
Функции decodeUserData и decodedCDNData декодируют ответ сервера для пользовательских данных и результат запроса CDN соответственно.
Далее нам нужны два сообщения, результаты запроса: type Msg
= DataSaved (Result Http.Error UserData)
| ImagesSaved (Result Http.Error CDNData)
Допустим, где-то в реализации функции обновления есть кусок кода, выполняющий сохранение данных.
Например, это может выглядеть так: update : Msg -> Model -> (Model, Cmd Msg)
update msg model
case Msg of
ClickedSomething ->
(model, Http.send DataSaved (save model.userData))
В этом случае создается запрос и отмечается сообщением DataSaved. Затем получено это сообщение: update : Msg -> Model -> (Model, Cmd Msg)
update msg model
case Msg of
DataSaved (Ok userData) ->
( {model | userData = userData}, Http.send ImagesSaved (saveImages userData.id model.images))
DataSaved (Err reason) ->
(model, Cmd.None)
Если сохранение прошло успешно, мы обновляем данные в модели и вызываем запрос на сохранение фотографий, куда передаем полученный идентификатор пользовательских данных.
Обработка сообщения ImagesSaved будет аналогична обработке DataSaved, и потребуется обрабатывать случаи успеха и неудачи.
Теперь рассмотрим вариант использования функции Http.toTask. Используя описанные функции, определяем новую функцию: saveAll : UserData -> Images -> Task Http.Error (UserData, CDNData)
saveAll : userData images =
save model.userData
|> Http.toTask
|> Task.andThen (\newUserData ->
saveImages usersData.id images
|> Http.toTask
|> Task.map (\newImages ->
(userData, newImages)
}
)
Теперь, используя возможность объединения задач, мы можем получать все данные в виде одного сообщения: type Msg
= Saved (Result Http.Error (UserData, CDNData))
update : Msg -> Model -> (Model, Cmd Msg)
update msg model
case Msg of
ClickedSomething ->
(model, Task.attempt Saved (saveAll model.userData model.images))
DataSaved (Ok (userData, images)) ->
( {model | userData = userData, images = images}, Cmd.none)
DataSaved (Err reason) ->
(model, Cmd.None)
Для выполнения запросов мы используем функцию Задача.
попытка , что позволяет выполнить задачу.
Не путать с функцией Задача.
выполнить .
Task.perform — позволяет выполнять задачи, которые не могу потерпеть неудачу .
Task.attempt — выполняет задачи, которые может потерпеть неудачу .
Этот подход более компактен с точки зрения количества сообщений, сложности функции обновления и позволяет сохранить логику более локальной.
В своих проектах, приложениях и компонентах я часто создаю модуль Commands.elm, в котором описываю функции взаимодействия с серверной частью вида.
-> Task Http.Error a.
Статус выполнения запроса
В процессе выполнения запросов часто приходится блокировать интерфейс полностью или частично, а также сообщать об ошибках выполнения запросов, если таковые имеются.В целом состояние запроса можно описать так:
- запрос не был выполнен;
- запрос выполнен;
- запрос выполнен успешно;
- запрос не удался.
Поначалу я активно им пользовался, но со временем наличие дополнительного типа Вебданные стал излишним и работать с ним утомительно.
Вместо этого пакета появились следующие правила:
- Объявите все данные с сервера как тип Maybe. В данном случае «Ничто» означает отсутствие данных;
- объявите атрибут загрузки типа Int в модели приложения или компонента.
Параметр хранит количество выполняемых запросов.
Единственное неудобство такого подхода — необходимость увеличивать и уменьшать атрибут в начале запроса и в конце соответственно;
- объявите атрибут ошибок типа List String в модели приложения или компонента.
Этот атрибут используется для хранения данных об ошибках.
Статус выполнения запроса должен включать ход загрузки из пакета.
Последовательность задач
Давайте рассмотрим варианты последовательностей задач, которые часто встречаются при разработке:- последовательные зависимые задачи;
- последовательные самостоятельные задачи;
- параллельные независимые задачи.
Последовательность задач прерывается при первом сбое и возвращает ошибку.
В случае успеха возвращается некоторая комбинация результатов: someTaskA
|> Task.andThen (\resultA ->
someTaskB
|> Task.map (\resultB ->
(resultA, resultB)
)
)
Этот код создает задачу типа Ошибка задачи (a, b), которую можно выполнить позже.
Функция Task.andThen позволяет отправить на выполнение новую задачу, если предыдущая выполнена успешно.
Функция Task.map позволяет конвертировать результаты выполнения в случае успеха.
Могут быть случаи, когда успешного выполнения задачи недостаточно и необходимо проверить целостность данных.
Предположим, что идентификаторы пользователей одинаковы: someTaskA
|> Task.andThen (\resultA ->
someTaskB
|> Task.andThen (\resultB ->
case resultA.userId == resultB.userId of
True ->
Task.succeed (resultA, resultB)
False ->
Task.fail “User is not the same”
)
)
Стоит отметить, что вместо функции Task.map используется функция Task.andThen и успешность выполнения второй задачи мы определяем сами с помощью функций Задача.
успех И Task.fail .
Если одна из задач может выйти из строя и это допустимо, то нужно использовать функцию Task.onError чтобы указать значение в случае ошибки: someTaskA
|> Task.onError (\msg -> Task,succeed defaultValue)
|> Task.andThen (\resultA ->
someTaskB
|> Task.map (\resultB ->
(resultA, resultB)
)
)
Вызов функции Task.onError должен быть объявлен сразу после объявления задачи.
Последовательные независимые запросы можно выполнять с помощью функций Task.mapN. Которые позволяют объединить несколько результатов задачи в один.
Первая задача, которая завершается неудачно, прерывает всю цепочку, поэтому используйте функцию Task.onError для значений по умолчанию.
Также обратите внимание на функцию Task.sequence , он позволяет выполнять ряд аналогичных задач.
В текущей реализации языка параллельные задачи не описаны.
Их реализация возможна на уровне приложения или компонента посредством обработки событий в функции обновления.
Вся логика остается на плечах разработчика.
Теги: #JavaScript #frontend #Функциональное программирование #node.js #elm
-
Нью-Йорк
19 Oct, 24 -
Ноутбук-Трансформер Acer Aspire 1425P
19 Oct, 24 -
Планшетный Пк С Windows – 10 Лет Прогресса
19 Oct, 24 -
Selectel: Жди Выходных!
19 Oct, 24 -
Осенняя Школа Semantic Web В Итмо
19 Oct, 24