Риболовът беше аналогия, която ми дойде на ум, когато се опитах да обясня проблем, който имах, на човек, който не е разработчик. Идеята, че една HTTP заявка е като изхвърляне на линия и чакане на ухапване или отговор, беше това, което измислих. Ако хванах риба, това беше код за състояние 200, в противен случай се отчита грешка при пропуснат опит. Намирането на аналогично събитие, сравнимо с грешка или лош отговор, беше малко по-трудно и не си струваше допълнителните усилия за обяснението. Разбирате смисъла. Сега към проблема, който имах, и моето „задълбочено гмуркане“ в разбирането на различни решения.

404 — Рибата не е намерена.

Проект, върху който работех, изискваше да обработя няколко файла, поискани от сървъра, но не можах да стартирам този процес, докато не ги получа всички. Сякаш бях в морето на риболовен кораб и имах изхвърлени няколко въдица и не можех да си събера багажа и да се прибера вкъщи, докато не ги намотах всичките.

С ES6 Promises има метод Promise.all, който приема масив (или който и да е итерируем) от Promises като параметър и връща Promise, който се разрешава с масив от всички отделни решения на Promises. Но доколкото разбирам, ако едно от Обещанията трябва да бъде отхвърлено, цялото произтичащо Обещание ще бъде отхвърлено. Обичам да наричам това „пълно отхвърляне“. Това нямаше да работи за мен. Ако не се закача на една от линиите, пак исках рибата от другите си линии. Естествено, реших да направя малко копаене и да изградя свой собствен начин за правене на резолюция с множество обещания.

Другата риба в морето

Работейки интензивно с AngularJS (1.x), реших да видя как $q.all()function се справя. В своята „документация“ те споменават, че $q „е съвместима с Promises/A+ реализация на обещания/отложени обекти, вдъхновена от „Q на Крис Ковал“.“ Сега, знаейки това, бях принуден да намеря една от най-ранните конструкции на този метод, които можех. В „източника“ на Q на Kowal той споменава, че методът Q.all() е създаден от Марк Милър. И така, още веднъж в заешката дупка открих привидно „оригинална реализация“ на резолюцията с множество обещания.

Нека да разгледаме как Милър прилага тази техника:

В сравнение с $q.all() и Q.all() този allFulfilled метод е теоретично същата концепция.

Не за моя изненада, можете да видите как той се справи с идеята за пълно отхвърлянев своя for цикъл. Ако Q(answerP).when() изпълни обратното извикване за грешка, цялото обещание deferredResult се разрешава като отхвърлено. Трябваше да намеря друг начин, така че продължих да се гмуркам.

Грациозният улов

Въпреки че чувствах, че може да бъде по-изразителен, намерих това „вдъхновение от StackOverflow“ за добър тласък в правилната посока към решение.

Реших, че улавянето на тези грешки и връщането им в рамките на масива от резултати е най-добрият курс за мен. Някои реализации на библиотеки вече имат това. Например when.js има метод settle, който връща масив от обекти, които съдържат свойство на състояние, подобно на преминаване-неуспех. Но усетих, че мога да измисля нещо малко по-интуитивно.

Лесният актьорски състав

За моята реализация трябваше да върна ArrayBuffer на всеки заявен файл. Основен XMLHttpRequest може да бъде макиран в много различни библиотеки, но реших, че искам да използвам строго обикновен JavaScript. С помощта на деструктурираните параметри на ES6 успях да създам обща заявка с някои параметри по подразбиране, които ще ми върнат Promise. Освен това MDN винаги съдържа отлична „документация“ във „Vanilla JS“, която да надграждам, когато имам нужда от справка.

ЗАБЕЛЕЖКА: Те все още се опитват да направят „Fetch“ да се случи. — „Зли момичета“

Методът на множество обещания

След като имах тази проста настройка на метода на заявка, реших да създам моя метод с множество обещания all. Сега бих искал да добавя това към класа Promise просто за по-лесно използване, но предпочитам „никога да не презаписвам обект, който не притежавам“. Заради този пример просто ще го оставя като общ метод.

Както можете да видите, подобно на внедряването allFulfilled на Марк Милър, аз преминавам през обещанията и добавям отговора към масив. Но ако получа грешка, моят catchcallback ще продължи да го добавя към масива, но ще създаде нова грешка, за да поддържа проследяването на стека, както и лесно да идентифицира това като грешка по-късно.

Тъй като асинхронните заявки могат да върнат отговор в различно време, общото обещание може да се разреши в различен ред от масива от обещания, който е бил предаден. За да запазя целостта на моя изходен масив, добавям моите отговори в позицията на индекса на promises масив или реда, съответстващ на който са били поискани, а не просто избутани в края на responses масива.

Основното обаждане

Опаковането на всичко заедно сега е толкова просто, колкото създаването на обещания на заявка, добавянето им към масив и направата на моя all метод да ги извика. Това общо обещание ще се разреши с моя набор от отговори, всеки от които може да бъде обработен според нуждите.

Откриването дали са възникнали грешки изисква само проста проверка, за да видите дали стойността е instanceOf Error клас. Сега, ако искам да се случи пълно отхвърляне, мога да throwmy Error в рамките на функцията за успех на общото обещание, но не съм принуден да харесвам с другите all методи.

Сега, в зависимост от обстоятелствата на първоначалния масив Promises, може да има необходими промени, за да отговарят на конкретния случай на употреба. Открих, че това решение работи добре в моята ситуация, но може да не е всеобхватно. Любопитен съм да видя други начини, по които този проблем е решен.

Тази статия първоначално беше публикувана на моя собствен сайт, patmigliaccio.com