Kami terus berbicara tentang Elm 0,18 .
Elm Nyaman dan canggung
Elm Nyaman dan canggung. Komposisi
Elm Nyaman dan canggung. Json.Encoder dan Json.Decoder
Pada artikel ini, kami mempertimbangkan masalah interaksi dengan sisi server.
Eksekusi query
Contoh pertanyaan sederhana dapat ditemukan dalam deskripsi paket Http .
Jenis permintaan adalah Http . Permintaan a .
Jenis hasil permintaan adalah Hasil Http.Error a.
Kedua jenis parameter oleh tipe pengguna, decoder yang harus ditentukan saat membuat permintaan.
Anda dapat menjalankan permintaan menggunakan fungsi:
- Http.send;
- Http.toTask.
Http.send memungkinkan Anda untuk mengeksekusi permintaan dan setelah selesai, meneruskan pesan ke fungsi pembaruan yang ditentukan dalam argumen pertama. Pesan membawa data tentang hasil permintaan.
Http.toTask memungkinkan Anda membuat Tugas dari permintaan yang dapat Anda jalankan. Menggunakan fungsi Http.toTask, menurut pendapat saya, adalah yang paling nyaman, karena instance Task dapat digabungkan menggunakan berbagai fungsi , misalnya Task.map2 .
Mari kita lihat sebuah contoh. Misalnya, untuk menyimpan data pengguna, dua kueri bergantung berurutan harus dijalankan. Biarkan itu membuat posting dari pengguna dan menyimpan foto kepadanya (beberapa CDN digunakan).
Pertama, pertimbangkan implementasi untuk kasus Http. Kirim. Untuk ini kita membutuhkan dua fungsi:
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
Tipe UserData dan CDNData tidak akan dijelaskan, misalnya, mereka tidak penting. Fungsi encodeUserData adalah encoder. saveImages menerima pengidentifikasi data pengguna, yang digunakan untuk membentuk alamat, dan daftar foto. Fungsi imagesBody membentuk badan permintaan dari tipe multipart / formulir-data . Fungsi decodeUserData dan decodedCDNData masing-masing mendekode respons server untuk data pengguna dan hasil permintaan CDN.
Selanjutnya, kita membutuhkan dua pesan, hasil permintaan:
type Msg = DataSaved (Result Http.Error UserData) | ImagesSaved (Result Http.Error CDNData)
Misalkan, di suatu tempat dalam pelaksanaan fungsi pembaruan, ada bagian kode yang menyimpan data. Misalnya, mungkin terlihat seperti ini:
update : Msg -> Model -> (Model, Cmd Msg) update msg model case Msg of ClickedSomething -> (model, Http.send DataSaved (save model.userData))
Dalam hal ini, kueri dibuat dan ditandai dengan pesan DataSaved. Selanjutnya, pesan ini diterima:
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)
Jika berhasil disimpan, kami memperbarui data dalam model dan memanggil permintaan untuk menyimpan foto tempat kami mentransfer pengidentifikasi data pengguna yang diterima. Memproses pesan ImagesSaved akan mirip dengan DataSaved, akan diperlukan untuk menangani kasus yang berhasil dan gagal.
Sekarang pertimbangkan opsi menggunakan fungsi Http.toTask. Menggunakan fungsi yang dijelaskan, kami mendefinisikan fungsi baru:
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) } )
Sekarang, menggunakan kemampuan untuk menggabungkan tugas, kita bisa mendapatkan semua data dalam satu pesan:
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)
Untuk menjalankan permintaan, kami menggunakan fungsi Task.attempt , yang memungkinkan Anda untuk menyelesaikan tugas. Jangan bingung dengan fungsi Task.perform . Task.perform - memungkinkan Anda untuk menyelesaikan tugas yang tidak dapat gagal . Task.attempt - Melakukan tugas yang mungkin gagal .
Pendekatan ini lebih kompak dalam hal jumlah pesan, kompleksitas fungsi pembaruan dan memungkinkan Anda untuk menjaga logikanya lebih lokal.
Dalam proyek saya, aplikasi dan komponen saya sering membuat modul Commands.elm, di mana saya menggambarkan fungsi berinteraksi dengan bagian server dengan jenis ... -> Tugas Http.Error a.
Meminta Status Eksekusi
Dalam proses mengeksekusi kueri, antarmuka sering harus diblokir secara keseluruhan atau sebagian, dan juga melaporkan kesalahan dalam eksekusi permintaan, jika ada. Secara umum, status permintaan dapat digambarkan sebagai:
- permintaan belum selesai;
- permintaan sedang berlangsung;
- Permintaan berhasil diselesaikan.
- permintaan gagal.
Untuk deskripsi seperti itu, ada paket RemoteData . Pada awalnya, saya secara aktif menggunakannya, tetapi seiring waktu, kehadiran jenis WebData tambahan menjadi berlebihan, dan bekerja dengannya sangat membosankan. Alih-alih paket ini, aturan berikut muncul:
- mendeklarasikan semua data dari server sebagai Maybe. Dalam hal ini, Tidak Ada, menunjukkan tidak adanya data;
- mendeklarasikan atribut memuat tipe Int dalam aplikasi atau model komponen. Parameter menyimpan jumlah permintaan yang dieksekusi. Satu-satunya ketidaknyamanan dari pendekatan ini adalah kebutuhan untuk menambah dan mengurangi atribut pada awal permintaan dan setelah selesai, masing-masing;
- mendeklarasikan atribut kesalahan tipe List String dalam aplikasi atau model komponen. Atribut ini digunakan untuk menyimpan data kesalahan.
Skema yang dijelaskan tidak jauh lebih baik daripada opsi dengan paket RemoteData, seperti yang ditunjukkan oleh latihan. Jika ada yang punya opsi lain, bagikan di komentar.
Status eksekusi permintaan harus mencakup progres unduhan dari paket Http.Progress .
Urutan tugas
Pertimbangkan opsi untuk urutan tugas yang sering ditemukan dalam pengembangan:
- tugas tergantung berurutan;
- tugas independen yang konsisten;
- tugas paralel paralel.
Tugas dependen yang berurutan telah dipertimbangkan di atas, saya akan memberikan gambaran umum dan pendekatan untuk implementasi di bagian ini.
Urutan tugas terputus pada kegagalan pertama dan kesalahan dikembalikan. Jika berhasil, beberapa kombinasi hasil dikembalikan:
someTaskA |> Task.andThen (\resultA -> someTaskB |> Task.map (\resultB -> (resultA, resultB) ) )
Kode ini membuat tugas ketik Kesalahan tugas (a, b), yang dapat dilakukan nanti.
Fungsi Task.andThen memungkinkan Anda untuk mentransfer tugas baru yang akan dieksekusi jika berhasil menyelesaikan yang sebelumnya. Fungsi Task.map memungkinkan Anda untuk mengkonversi hasil eksekusi melon jika berhasil.
Ada opsi ketika penyelesaian tugas yang berhasil tidak akan cukup dan Anda perlu memeriksa konsistensi data. Asumsikan ID pengguna cocok:
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β ) )
Perlu dicatat bahwa alih-alih fungsi Task.map, fungsi Task.andThen digunakan dan keberhasilan tugas kedua ditentukan secara independen menggunakan fungsi Task.succeed dan Task.fail .
Jika salah satu tugas dapat gagal dan ini dapat diterima, maka Anda perlu menggunakan fungsi Task.onError untuk menunjukkan nilai jika terjadi kesalahan:
someTaskA |> Task.onError (\msg -> Task,succeed defaultValue) |> Task.andThen (\resultA -> someTaskB |> Task.map (\resultB -> (resultA, resultB) ) )
Panggilan ke fungsi Task.onError harus dinyatakan segera setelah tugas dinyatakan.
Kueri independen yang berurutan dapat dilakukan menggunakan fungsi Task.mapN. Yang memungkinkan Anda untuk menggabungkan beberapa hasil tugas menjadi satu. Tugas jatuh pertama mengganggu eksekusi seluruh rantai, jadi gunakan fungsi Task.onError untuk nilai default. Juga periksa fungsi Task.afterence , ini memungkinkan Anda untuk melakukan serangkaian tugas serupa.
Tugas paralel dalam implementasi bahasa saat ini tidak dijelaskan. Implementasinya dimungkinkan pada tingkat aplikasi atau komponen melalui pemrosesan acara dalam fungsi pembaruan. Semua logika tetap berada di pundak pengembang.