Асинхронный код в синхронный встроенными средствами

Итак, не так давно мне пришлось столкнуться с довольно популярной задачей преобразования асинхронного кода в синхронный в рамках цикла. В моем случае это была работа с AmazonAPI методами productSearch. Все бы ничего, да вот только данный API очень не любит когда к нему обращаются слишком часто, а мне было необходимо в цикле опрашивать состояние продуктов.

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

Для реализации нам понадобятся: promises (я буду использовать библиотеку q), версия nodejs с поддержкой генераторов, подопытная асинхронная функция (будем использовать setTimeout).

var q = require("q");

function* itemGenerator(data)
{
  var i, len = data.length;
  for(i = 0; i < len; i++)
  {
    yield data[i];
  }
}

function oldIterator(data)
{
  var i = -1, len = data.length;
  return {
    "next": function()
    {
      i++;
      return {
        "done": i == len,
        "value": data[i]
      };
    }
  }
}

function main()
{
  var def = q.defer(), items = [1, 2, 3, 4, 5];

  (function foo(gen)
  {
    var genItem = gen.next(), item = genItem.value;
    if(genItem.done)
    {
      def.resolve(true);
      return;
    }

    console.log("start call for", item);
    setTimeout(function()
    {
      console.log("end call for", item);
      foo(gen);
    });

  })(itemGenerator(items))

  return def.promise;
}

main().then(function(flag)
{
  console.log("promise has been resolved", flag);
});

результатом выполнения данного скрипта будет:

> node async_sync.js
start call for 1
end call for 1
start call for 2
end call for 2
start call for 3
end call for 3
start call for 4
end call for 4
start call for 5
end call for 5
promise has been resolved  true

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

PS: данный код будет работать аналогично и в JavaScript, достаточно заменить библиотеку «Q» на родные «Promise».

На этом все, спасибо за внимание!

Комментарии (0)

© Habrahabr.ru