8.25.2016

Асинхронный бесконечный цикл на NodeJS

Всем привет. Решил сделать небольшую памятку по следам недавних событий. 

Живой пример: https://jsfiddle.net/popy8apd/

Неделю назад мой коллега решил сделать парсер поисковой выдачи на NodeJS и selenium, натолкнулся на трудности работы с асинхронным кодом, "плюнул" и написал на Python. 

А проблема заключалась вот в чем. Он пытался, по старой PHP привычке, писать код в синхронном стиле. Выполнял поисковой запрос в google потом запускал цикл for и на каждой итерации собирал ссылки с первой страницы поиска, на второй итерации на второй странице и так далее. 

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

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

Пусть переход на новую страницу выдачи занимает секунду и еще две секунды занимает сохранение результатов в базу (ну чтоб было заметнее).

Пример на jsfiddle => https://jsfiddle.net/popy8apd/

function* generateSequence() {
    for (let i = 0; i < 5; i++) { //
for (let i = 0; true; i++ ) - бесконечный
         let action = yield new Promise(function (resolve, reject) {
            // click on button in browser and wait load
            setTimeout(function () {
                resolve(i);
            }, 1000);
        });
        if (action === "break") break;
    }
}

let generator = generateSequence();

function go(action) {
    let promise = generator.next(action).value;
    promise.then((res) => {
        // page loaded save links to db
        alert(res);
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve(res + "SAVE");
            }, 2000);
        });
    }).then((res) => {
        // data from link saved in db go next page
        alert(res);
        // check condition break move or not
        go("break");
    });
}

go();