Pull to refresh
7
0
Артём Николаев @ArtemNIkolaev

Full Stack Web Developer

Send message

Вы всё-таки не поняли что я имел ввиду, позвольте уточнить. По другому первый пример можно представить вот так:


Другая версия первого примера
function doFinalThingCallback(err) {
    if (err) { handleAnotherError(err); }
    // Выполнено
} // конец doFinalThing

function doMoreStuffCallback(responseThree, err) {
    if (err) { handleAnotherError(err); }
    doFinalThing(responseThree, doFinalThingCallback);
} // конец doMoreStuff

function doSomethingElseCallback(responseTwo, err) {
    if (err) { handleError(err); }
    doMoreStuff(responseTwo, doMoreStuffCallback);
} // конец doSomethingElse

function doSomethingCallback(responseOne) {  
    doSomethingElse(responseOne, doSomethingElseCallback);
} // конец doSomething

doSomething(doSomethingCallback); // конец doSomething

Теперь функции НЕ анонимные. Но последовательность выполнения по прежнему не очевидна. Я бы сформулировал эту проблему так:


  • ты либо видишь кодовую ёлку(пример с коллбеками из предыдущего поста), в которой тяжело что-либо понять, но весь процесс в одном месте,
  • либо код красивый(текущий пример), но что бы увидеть последовательность нужно по всем этим функциям пробежаться.

С промисами ситуация абсолютно обратная


еще раз пример с промисами
doSomething()  
  .then(doSomethingElse)
  .catch(handleError)
  .then(doMoreStuff)
  .then(doFinalThing)
  .catch(handleAnotherError)

Функции могут быть анонимными или именными, сути дела это не меняет — в одном месте кода находится полная последовательность выполнения. Представьте то время, которое вы НЕ потратите с промисами, разбирая чужой код...

Ответ на ваш вопрос можно найти в статье У нас проблемы с промисами или вот под спойлером:


Продвинутая ошибка №3 — промисы против фабрик промисов

Допустим, вы хотите выполнить серию промисов один за другим, последовательно. Вы хотите что-то вроде Promise.all(), но такой, чтобы не выполнял промисы параллельно.


Сгоряча вы можете написать что-то подобное:


function executeSequentially(promises) {
  var result = Promise.resolve();
  promises.forEach(function (promise) {
    result = result.then(promise);
  });
  return result;
}

К сожалению, пример выше не будет работать так, как задумывалось. Промисы из списка, переданного в executeSequentially(), все равно начнут выполняться параллельно.


Причина в том, что по спецификации промис начинает выполнять заложенную в него логику сразу после создания. Он не будет ждать. Таким образом, не сами промисы, а массив фабрик промисов — это то, что действительно нужно передать в executeSequentially:


function executeSequentially(promiseFactories) {
  var result = Promise.resolve();
  promiseFactories.forEach(function (promiseFactory) {
    result = result.then(promiseFactory);
  });
  return result;
}

Я знаю, вы сейчас думаете: «Кто, черт возьми, этот Java программист, и почему он рассказывает нам о фабриках?». На самом деле фабрика — это простая функция, возвращающая промис:


function myPromiseFactory() {
  return somethingThatCreatesAPromise();
}

Почему этот пример будет работать? А потому, что наша фабрика не создаст промис до тех пор, пока до него не дойдет очередь. Она работает именно как resolveHandler для then().


Посмотрите внимательно на функцию executeSequentially() и мысленно замените ссылку на promiseFactory ее содержимым — сейчас над вашей головой должна радостно вспыхнуть лампочка :)

Не согласен, промисы при любом раскладе сильно лучше колбеков. Дождитесь перевода второй статьи Promises 102, где подробно рассказано о том, как их готовить. Так же рекомендую вам ознакомиться со статьёй У нас проблемы с промисами. Автор рассматривает всевозможные проблемы, которые могут возникнуть и возникают при неправильном использовании промисов.
По поводу вашего вопроса, я уже ссылался выше на статью Async/Await в javascript. Взгляд со стороны


Конкретно на ваш вопрос отвечает следующая цитата:


А если мне нужно одновременно получить результат от нескольких вызовов?

Так как мы уже разобрались, что мы имеем дело с Promise. Следовательно можно использовать метод .all() объекта Promise для решения такого рода задач.


async function unicorn() {
  let [rainbow, food] = await Promise.all([getRainbow(), getFood()]);
  return {rainbow, food}
}

Тут я с вами не соглашусь. Допустим, вам предлагают на выбор один из двух проектов:


Пример кода из первого проекта
doSomething(function(responseOne) {  
    doSomethingElse(responseOne, function(responseTwo, err) {
        if (err) { handleError(err); }
        doMoreStuff(responseTwo, function(responseThree, err) {
            if (err) { handleAnotherError(err); }
            doFinalThing(responseThree, function(err) {
                if (err) { handleAnotherError(err); }
                // Выполнено
            }); // конец doFinalThing
        }); // конец doMoreStuff
    }); // конец doSomethingElse
}); // конец doSomething

Пример кода из второго проекта
doSomething()  
  .then(doSomethingElse)
  .catch(handleError)
  .then(doMoreStuff)
  .then(doFinalThing)
  .catch(handleAnotherError)

Который вы выберете?
Единственную ассоциацию, которую вызывает у меня первый пример кода это: "Хочется взять, и переписать!", в то время, как во втором я фрагменте я отчетливо вижу что после чего происходит.

Позвольте процитировать другую статью на хабре — Async/Await в javascript. Взгляд со стороны:


Async/Await в javascript. Взгляд со стороны

Говоря общедоступным языком async/await — это Promise.


Когда вы объявляете функцию как асинхронную, через волшебное слово async, вы говорите, что данная функция возвращает Promise. Каждая вещь которую вы ожидаете внутри этой функции, используя волшебное слово await, то же возвращает Promise.


Из этого можно сделать вывод о том, что async/await являются в большей степени синтаксическим сахаром для промисов. Функция синхронной не становится, поток при этом не блокируется.

  1. Да, функции остаются асинхронными.
  2. Да, поток не будет блокироваться.
  3. И да и нет. В простейших ситуациях промисы действительно больше похожи на удобную обёртку. Однако уже .then() и .catch() дают нам мощный инструмент для управления последовательностью выполнения. А кроме этого есть еще Promise.all() и Promise.race(). Эти методы подробно рассматриваются во второй статье Promises 102(ссылка на оригинал, статья будет переведена в течение ближайшей недели).

Information

Rating
Does not participate
Location
Минск, Минская обл., Беларусь
Date of birth
Registered
Activity