В моем предыдущем сообщении в блоге я объяснил основы работы с генераторами в ES6 Javascript. Если вы еще не читали, вы можете проверить это здесь 👉 Общие сведения о генераторах в ES6 Javascript
Многие из вас просили о реальных примерах использования генераторов, поэтому я собираюсь показать одну из проблем, с которыми я столкнулся.
Введение
Но чтобы объяснить проблему, я должен сказать несколько слов о нашем продукте Mews Navigator, над которым мы работаем.
Навигатор позволяет вам регистрироваться онлайн, надежно сохраняя данные вашей кредитной карты и предоставляя вам полный контроль над информацией, которой вы хотите поделиться.
А теперь представьте, что вы делаете онлайн-регистрацию через приложение и переходите к этапу оплаты.
Итак, как только вы нажмете на следующую кнопку, вы увидите загрузчик, а затем список ваших платежных карт, довольно прямо, не так ли?
Отображение маршрута платежа
На самом деле, под капотом все немного сложнее. Перед отрисовкой компонента необходимо выполнить несколько шагов.
// Let's say user goes to this url: // www.mews.li/navigator/check-in/payment/:reservationId
// 1. This will check if the user is signed in. // If yes go render <Dashboard /> if not go to <SignIn /> authAction(); // async
// 2. We need to fetch the reservations fetchReservations(); // async
// 3. We need to check if `reservationId` and // route itself is valid (If all checks pass go to next one) isReservationIdValid({ reservations, currentReservation }); // sync
// 4. Fetch paymentcards fetchPaymentCards(); // async
// 5. Fetching hotel entitites fetchHotels(); // async
// 6. Some of hotels uses PCI proxy vault, if it does, // we need to initialize PCI proxy script. doesHotelUsePciProxy({ hotels, hotelId }); // sync
// 7. Fetch and init the script initPciProxy(); // async
У нас есть несколько проверок и выборка некоторых API перед рендерингом компонента.
Загвоздка в том, что если какая-либо из этих проверок может завершиться неудачно, и в зависимости от того, какие проверки не пройдут, мы перенаправим на конкретный случай.
Итак, как решить эту проблему без использования каких-либо внешних библиотек? Помните прошлый раз, когда я показывал вам этот пример?
function* avengersGenerator() { yield "Hulk"; // Pausing the execution yield "Thor"; yield "Iron man"; return "Ultron"; // Exiting of finishing the generator yield "Spiderman"; }
const iterator = avengersGenerator();
iterator.next();
Взгляните на выражение return
. Это остановит выполнение и проигнорирует все, что находится после оператора return
.
Это может дать нам возможность перебирать обещания и отменять их в любом месте цепочки обещаний.
Доказательство концепции
Давайте создадим что-то достаточно общее для нашего варианта использования, чтобы решить этот случай при маршрутизации. Основными пунктами были:
- Возможность работать с функциями синхронизации и асинхронности (вызовы API)
- Код вернул перенаправление, как только некоторые проверки не прошли.
- Достаточно общий, поэтому мы можем использовать его и для других маршрутов.
Итак, я открыл песочницу кода и придумал такое решение 👉 Codesandbox
Как вы можете видеть в консоли, у нас есть несколько действий и несколько проверок. Мы можем обойти проверку, которая должна завершиться ошибкой, а остальная часть кода не выполняется.
А вот и пример реализации маршрута шага платежа в коде.
function* paymentRouteGenerator() { yield authAction(); yield fetchReservations(); yield isReservationIdValid();
yield fetchPaymentCards(); yield fetchHotels(); yield doesHotelUsePciProxy({ hotelId }); yield initPciProxy(); }
const CHECK_IN_PAYMENT_ROUTE = { name: Route.CheckInPayment, path: "/:reservationId", action: resolveAction( generatorWrapper(paymentRouteGenerator), renderComponent(() => <CheckInPaymentStep />) ) };
Пришлось написать обработчик для нашего генератора. Это место, где случается magic
. Я объяснил каждый шаг ниже в комментариях.
const generatorWrapper = generator => context => { // 1. Creating an iterator const iterator = generator(context);
// 3. This function except yielded as a argument const handle = yielded => { const handleWithRedirectCheck = route => { // 4. Here is where the magic happens, we check if there is a redirect, if yes, // it would redirect (cancel) and will not execute the rest of the generator if (get("redirect", route)) { return route; } // Otherwise continue return handle(iterator.next()); }; // Exit if we are at the end of the generator if (yielded.done) { return; }
// Handling the async case if action/check is a promise if (isPromise(yielded.value)) { return yielded.value.then(handleWithRedirectCheck); } // If its not a promise, we can call handleWithRedirectCheck directly return handleWithRedirectCheck(yielded.value); };
// 2. Handling the iterator return handle(iterator.next()); };
Пока я просто играю с этим, поэтому, если у вас есть идеи, как решить эту проблему лучше, обязательно дайте мне знать. 😉
Спасибо за чтение
Сообщите мне в разделе комментариев, что вы думаете об этой серии генераторов. Если вам это нравится, вы знаете, что делать! Поделись с друзьями и коллегами.
Если вы хотите, чтобы я затронул некоторые темы в следующем посте, напишите мне в DM на dev.to или в твиттере @phung_cz, или если у вас есть какие-либо предложения, не стесняйтесь комментировать ниже.
Увидимся в следующий раз и продолжай взламывать ✌
Посмотрите, что мы создаем @ Mews systems, мы также нанимаем разработчиков и людей на другие должности. Напишите мне, если у вас возникнут вопросы.