Асинхронный код в синхронный встроенными средствами
Итак, не так давно мне пришлось столкнуться с довольно популярной задачей преобразования асинхронного кода в синхронный в рамках цикла. В моем случае это была работа с 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».
На этом все, спасибо за внимание!