الدردار. مريح ومحرج. Http ، المهمة

نواصل الحديث عن Elm 0.18 .


الدردار. مريح ومحرج
الدردار. مريح ومحرج. تكوين
الدردار. مريح ومحرج. Json.Encoder و Json.Decoder


في هذه المقالة ، نعتبر قضايا التفاعل مع جانب الخادم.


تنفيذ الاستعلام


يمكن العثور على أمثلة للاستعلامات البسيطة في وصف حزمة Http .


نوع الطلب هو Http.Request a .
نوع نتيجة الاستعلام هو نتيجة Http.Error
يتم تحديد معلمات كلا النوعين حسب نوع المستخدم ، ويجب تحديد مفكك التشفير عند إنشاء الطلب.


يمكنك تنفيذ الطلب باستخدام الوظائف:


  1. إرسال.
  2. Http.toTask.

يسمح لك Http.send بتنفيذ الطلب وعند اكتماله ، يمرر الرسالة إلى وظيفة التحديث المحددة في الوسيطة الأولى. تحمل الرسالة بيانات حول نتيجة الطلب.


يتيح لك Http.toTask إنشاء مهمة من طلب يمكنك تنفيذه. في رأيي ، يعد استخدام دالة Http.toTask هو الأكثر ملاءمة ، حيث يمكن دمج مثيلات المهام باستخدام وظائف مختلفة ، على سبيل المثال Task.map2 .


دعونا نلقي نظرة على مثال. على سبيل المثال ، لحفظ بيانات المستخدم ، يجب تنفيذ استعلامين تابعين متتاليين. فليكن إنشاء مشاركة من المستخدم وحفظ الصور له (يتم استخدام بعض CDN).


أولاً ، ضع في الاعتبار تنفيذ حالة Http.Send. لهذا نحن بحاجة إلى وظيفتين:


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 

لن يتم وصف أنواع UserData و CDNData ؛ على سبيل المثال ، فهي ليست مهمة. الوظيفة encodeUserData هي ترميز. يقبل saveImages معرف بيانات المستخدم ، الذي يُستخدم لتكوين العنوان وقائمة بالصور. تشكل وظيفة imagesBody نص طلب من نوع بيانات الأجزاء المتعددة / النماذج . تقوم الدالتان 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.attempt ، والتي تتيح لك إكمال المهمة. يجب عدم الخلط بينه وبين وظيفة Task.perform . Task.perform - يسمح لك بإكمال المهام التي لا يمكن أن تفشل . Task.attempt - تنفيذ المهام التي قد تفشل .


هذا النهج أكثر إحكاما من حيث عدد الرسائل ، وتعقيد وظيفة التحديث ويسمح لك بالحفاظ على المنطق أكثر محلية.


في المشاريع والتطبيقات والمكونات الخاصة بي ، غالبًا ما أقوم بإنشاء وحدة Commands.elm ، التي أصف فيها وظائف التفاعل مع جزء الخادم بنوع ... -> Task Http.Error a.


طلب تنفيذ حالة


في عملية تنفيذ الاستعلام ، غالبًا ما يجب حظر الواجهة كليًا أو جزئيًا ، وكذلك الإبلاغ عن الأخطاء في تنفيذ الاستعلام ، إن وجدت. بشكل عام ، يمكن وصف حالة الطلب على النحو التالي:


  1. فشل الطلب
  2. الطلب قيد التقدم ؛
  3. تم إكمال الطلب بنجاح.
  4. فشل الطلب.

لمثل هذا الوصف ، هناك حزمة RemoteData . في البداية ، استخدمتها بنشاط ، ولكن مع مرور الوقت ، أصبح وجود نوع إضافي من WebData زائدة عن الحاجة ، وكان العمل معها مملاً. بدلاً من هذه الحزمة ، ظهرت القواعد التالية:


  1. أعلن كافة البيانات من الخادم ربما. في هذه الحالة ، لا شيء ، يشير إلى عدم وجود البيانات ؛
  2. التصريح عن سمة تحميل من النوع Int في نموذج التطبيق أو المكون. تخزن المعلمة عدد الطلبات المنفذة. الإزعاج الوحيد لهذا النهج هو الحاجة إلى زيادة وإنقاص السمة في بداية الطلب وعند الانتهاء ، على التوالي ؛
  3. قم بتعريف سمة خطأ من نوع List String في التطبيق أو نموذج المكون. يتم استخدام هذه السمة لتخزين بيانات الخطأ.

المخطط الموصوف ليس أفضل بكثير من الخيار مع حزمة RemoteData ، كما تظهر الممارسة. إذا كان لدى أي شخص خيارات أخرى ، فشارك في التعليقات.


يجب أن تتضمن حالة تنفيذ الطلب تقدم التنزيل من حزمة Http.Progress .


تسلسل المهام


فكر في خيارات تسلسل المهام التي غالبًا ما توجد في التطوير:


  1. المهام التابعة التسلسلية ؛
  2. مهام مستقلة متسقة ؛
  3. مهام مستقلة موازية.

تم بالفعل النظر في المهام التابعة المتعاقبة أعلاه ؛ وسأقدم وصفًا عامًا وأساليب التنفيذ في هذا القسم.


تمت مقاطعة تسلسل المهام عند الفشل الأول ويتم إرجاع خطأ. في حالة النجاح ، يتم إرجاع مجموعة من النتائج:


 someTaskA |> Task.andThen (\resultA -> someTaskB |> Task.map (\resultB -> (resultA, resultB) ) ) 

ينشئ هذا الرمز مهمة من نوع Task error (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.succeed و 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 ، فهي تتيح لك تنفيذ سلسلة من المهام المماثلة.


لم يتم وصف المهام الموازية في تنفيذ اللغة الحالية. يمكن تنفيذها على مستوى التطبيق أو المكون من خلال معالجة الأحداث في وظيفة التحديث. كل المنطق يبقى على أكتاف المطور.

Source: https://habr.com/ru/post/ar424979/


All Articles