Всем привет. Решил сделать небольшую памятку по следам недавних событий.
Неделю назад мой коллега решил сделать парсер поисковой выдачи на 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();