Тост за деструктуризацию ES2015
07.03.2017

Тост за деструктуризацию ES2015

Оригинал: A toast to ES2015 destructuring, Phil Nash

Я считаю синтаксис деструктуризации ES2015 очень клевым. Возможно, он играет не такую важную роль, как промисы, генераторы или синтаксис классов, но я нахожу его очень полезным. Он описан удивительно подробно.

К сожалению, большинство примеров, которые я нашел, фокусируются только на синтаксисе, а не на применении в реальной жизни. Вот пример с MDN:


var a, b;
[a, b] = [10, 20];
console.log(a); // 10
console.log(b); // 20

К счастью, у меня есть примеры использования деструктуризации в реальной жизни.

Обработка промисов

Один из моих любимых вариантов использования деструктуризации — с методом Promise.all, который ожидает завершения нескольких асинхронных задач. Результатом Promise.all является массив, вы можете либо пройтись по нему циклом, либо выбрать нужный результат вручную.

Но если заранее известно, какие объекты будут содержаться в массиве, можно использовать деструктуризацию, чтобы сделать жизнь проще, а код лучше. Рассмотрим пример.

Сравнение пива

Допустим, мы хотим сравнить два вкусных вида пива в сайта BrewDog, что я в реальной жизни делаю постоянно. Мы можем получить информацию о них с помощью Punk API Сэма Мейсона (Sam Mason). Мы будем использовать fetch API, чтобы получить данные о каждом виде пива. И нам нужны будут результаты обоих запросов, чтобы сравнить два вида пива.

Посмотрим на код:


const punkIPAUrl = "https://api.punkapi.com/v2/beers/106";
const deadPonyClubUrl = "https://api.punkapi.com/v2/beers/91";
const punkIPAPromise = fetch(punkIPAUrl)
    .then(res => res.json())
    .then(data => data[0]);
const deadPonyClubPromise = fetch(deadPonyClubUrl)
    .then(res => res.json())
    .then(data => data[0]);

Promise.all([punkIPAPromise, deadPonyClubPromise])
    .then(beers => {
        const punkIPA = beers[0];
        const deadPonyClub = beers[1];
        const stronger = (punkIPA.abv < deadPonyClub.abv ? deadPonyClub.name : punkIPA.name) + " is stronger";
        console.log(stronger);
    });

Код выше можно улучшить, используя деструктуризацию:


Promise.all([punkIPAPromise, deadPonyClubPromise])
    .then(([punkIPA, deadPonyClub]) => {
        const stronger = (punkIPA.abv < deadPonyClub.abv ? deadPonyClub.name : punkIPA.name) + " is stronger";
        console.log(stronger);
    });

Мы знаем, что в результате получим два вида пива, знаем, какое из них какое. Поэтому можно использовать деструктуризацию, чтобы получить данные о каждом пиве в переменные, вместо того, чтобы доставать их из массива.

Больше примеров

Хорошо, пример выше уже ближе к реальной жизни. Впервые я нашел применение этой технике, когда писал о сервис воркерах на 12 Devs of Christmas. Она оказалась полезной в методе returnFromCacheOrFetch, который реализует кеширование.

Этот метод открывает именованный кеш и пытается сопоставить текущий запрос с кешем. Перед тем, как вернуть результат, он инициализирует запрос для текущего ресурса и кеширует результат. В итоге, если запрос найден в кеше, вернется закешированный результат, в противном случае вернется новый запрос fetch. Подробнее об этом вы можете прочитать в оригинальном посте.

Финальный код выглядит так:


function returnFromCacheOrFetch(request, cacheName) {
    const cachePromise = caches.open(cacheName);
    const matchPromise = cachePromise
        .then((cache) => cache.match(request));

    /* Use the result of both the above Promises to return the
       Response. Promise.all returns an array, but we destructure 
       that in the callback. */
    return Promise.all([cachePromise, matchPromise])
        .then(([cache, cacheResponse]) => {
            // Kick off the update request
            const fetchPromise = fetch(request)
                .then((fetchResponse) => {
                    /* Cache the updated file 
                       and then return the response */
                    cache.put(request, fetchResponse.clone());
                    return fetchResponse;
                });
            /* return the cached response if we have it, 
          otherwise the result of the fetch. */
            return cacheResponse || fetchPromise;
    });
}

В примере мне нужны были результаты caches.open и cache.match(request) одновременно, чтобы запустить фоновый запрос и вернуть закешированный результат. Я использовал их вместе в Promise.all и с помощью деструктуризации получил результат, сохранив код аккуратным.

Именование вещей

В примерах в статье деструктурирование параметров позволило нам именовать результаты, которые мы ожидаем получить от промисов, переданных в Promise.all. Фактически везде, где мы используем деструктуризацию, особенно с массивами, это делает код более читабельным и более удобным в долгосрочной перспективе.

Есть ли другие примеры, когда синтаксис деструктуризации ES2015 оказывается полезным? Я хотел бы узнать, как вы используете эту возможность, поэтому, пожалуйста, поделитесь своими советами со мной в Twitter.