Подтвердить что ты не робот

JavaScript асинхронное программирование: promises против генераторов

Promises и генераторы позволяют вам писать асинхронный код. Я не понимаю, почему оба этих механизма введены в ECMA script 6. Когда лучше всего использовать promises, а когда генераторы?

4b9b3361

Ответ 1

Между этими двумя методами нет никакой оппозиции. Они сосуществуют друг с другом, прекрасно дополняя друг друга. Promises дает вам возможность получить результат асинхронной операции, которая пока недоступна. Он решает проблему Pyramid of Doom. Поэтому вместо:

function ourImportantFunction(callback) {
  //... some code 1
  task1(function(val1) {
    //... some code 2
    task2(val1, function(val2) {
      //... some code 3
      task3(val2, callback);
    });
  });
}

вы можете написать:

function ourImportantFunction() {
  return Promise.resolve()
    .then(function() {
        //... some code 1
        return task1(val3)
    })
    .then(function(val2) {
        //... some code 2
        return task2(val2)
    })
    .then(function(val2) {
        //... some code 3
        return task3(val2);
    });
}

ourImportantFunction().then(callback);

Но даже с Promises вы должны писать код в асинхронном режиме - вы всегда должны передавать обратные вызовы в функции. Написание асинхронного кода намного сложнее, чем синхронно. Даже при Promises, когда код огромен, становится сложно увидеть алгоритм (ну, это очень субъективно, с ним можно спорить, но для большинства программистов я думаю, что это правда). Поэтому мы хотим синхронно писать асинхронный код. То, что генераторы приходят нам на помощь. Поэтому вместо кода выше вы можете написать:

var ourImportantFunction = spawn(function*() {
    //... some code 1
    var val1 = yield task1();
    //... some code 2
    var val2 = yield task2(val1);
    //... some code 3
    var val3 = yield task3(val2);

    return val3;
});

ourImportantFunction().then(callback);

где простейшая возможная реализация spawn может быть примерно такой:

function spawn(generator) {
  return function() {    
    var iter = generator.apply(this, arguments);

    return Promise.resolve().then(function onValue(lastValue){
      var result = iter.next(lastValue); 

      var done  = result.done;
      var value = result.value;

      if (done) return value; // generator done, resolve promise
      return Promise.resolve(value).then(onValue, iter.throw.bind(iter)); // repeat
    });
  };
}

Как видите, value (результат некоторой асинхронной функции task{N}) должен быть обещанием. Вы не можете сделать это с помощью обратных вызовов.

Остается только реализовать технику spawn в самом языке. Таким образом, мы заменяем spawn на async и yield на await и переходим к ES7 async/await:

var ourImportantFunction = async function() {
    //... some code 1
    var val1 = await task1();
    //... some code 2
    var val2 = await task2(val1);
    //... some code 3
    var val3 = await task3(val2);

    return val3;
}

Я рекомендую вам посмотреть это видео, чтобы больше понять это и некоторые другие приходящие методы. Если парень говорит слишком быстро для вас, замедлите скорость игры ( "настройки" в правом нижнем углу или просто нажмите [ shift + <])

Что лучше: просто обратные вызовы или promises или Promises с генераторами - это очень субъективный вопрос. Обратные вызовы - это самое быстрое решение в настоящее время (производительность native Promises сейчас очень плохая). Promises с генераторами дает вам возможность синхронно писать асинхронный код. Но пока они намного медленнее, чем простые обратные вызовы.

Ответ 2

Promises, а генераторы - разные шаблоны (конструкции) программного обеспечения:

На самом деле генераторы не являются асинхронными.

Генераторы полезны, когда вам нужно получить серию значений не сразу, а один за каждый запрос. Генератор будет возвращать следующее значение немедленно (синхронно) на каждый вызов до тех пор, пока он не достигнет конца последовательности (или бесконечный в случае бесконечных рядов).

Promises полезны, когда вам нужно "отложить" значение, которое еще не может быть вычислено (или может быть недоступно). Когда значение доступно - это целое значение (а не его часть), даже если это массив или другое сложное значение.

Вы можете увидеть более подробную информацию и примеры в статьях в Википедии.