Страницы

Поиск по вопросам

суббота, 4 января 2020 г.

Использование promise с циклом for

#javascript #nodejs #async_programming #promise


Задача: собираю данные геодаты с сервера (не моего). Для этого формирую запрос для
определенного промежутка координат и в цикле for отправляю запрос на сервер. Ответ
записываю в файл и дабы сервер не банил меняю ip адрес через tor-control. Ожидание
ответа реализовано с использованием promise. Это в идеале. На деле (судя по логам в
консоли) данные отправляются как попало. Подскажите где я повернул не туда.

for (var i = xMin; i <= xMax; i++) {
    for (var k = yMin; k <= yMax; k++) {
        var swp = swPoint(k, i);
        var nep = nePoint(k, i);
        var llsw = pointToLatLong(swp[0], swp[1]);
        var llne = pointToLatLong(nep[0], nep[1]);
        var datatosend = 'fromlat=' + llsw[0].toString() + 'tolat=' + llne[0].toString()
+ 'fromlng=' + llsw[1].toString() + 'tolng' + llne[1].toString();

        currX = k;
        currY = i;

        filename = "json/" + currX.toString() + "_" + currY.toString() + "_" + zoom.toString()
+ ".json";

        options.body = datatosend;

        console.log(filename);

        GetData(options)
        .then(body =>{
            return WriteToFile(body, filename);
        })
        .then(() =>{
            return GetNewCircuit();
        })
        .then(() =>{
            console.log('Step finished');
        })
    }
}


Судя по логам получается что он 4 раза проносится по циклу не дожидаясь выполнения
предыдущих операций. 

Текст лога консоли:

json/0_0_1.json
json/1_0_1.json
json/0_1_1.json
json/1_1_1.json
Get response
json/1_1_1.json
Circuit changed
Step finished
Get response
json/1_1_1.json
Circuit changed
Step finished
Get response
json/1_1_1.json
Circuit changed
Step finished
Get response
json/1_1_1.json
Circuit changed
Step finished


Вызываемые методы:

function GetNewCircuit(){
    return new promise(function(resolve, reject){
        new_identity('127.0.0.1', 9051, cookie, function(err){
            if(!err){
               console.log('Circuit changed');
               resolve()
            } else{
                console.log(err);
                reject(err);
            }
        });
    });
}

function GetData(reqOptions){
    return new promise(function(resolve, reject){
        request(reqOptions, function(error,response,body){
            if(!error && response.statusCode == 200){
                console.log('Get response');
                resolve(body);
            }
            else{
                reject(error);
            }
        });
    });
}

function WriteToFile(dataToWrite, fileToWrite){
    return new promise(function(resolve, reject) {
        fs.writeFile(fileToWrite, dataToWrite, function(err) {
           if(!err){
               console.log(fileToWrite);
               resolve();
           }
           else{
               reject(err);
           }
        }); 
    });
}


С js'ом знакомство только начинаю, поэтому если кроме ответа еще и посоветуете где
и как лучше ознакамливаться буду только рад.
    


Ответы

Ответ 1



В коде этом сразу две основных проблемы (из трех) начинающих javascript-разработчиков вижу я. Проблема один - непонимание асинхронности. Разумеется, цикл не ждет возврата результата асинхронной операции, чтобы прокрутиться дальше. Представьте что у вас четыре гиперактивных двортерьера, и вы подряд кидаете им четыре мячика. В каком порядке они их принесут обратно? Да черт его знает. А то что внутри Promise.then, это колбек, только написанный удобнее. Что делать? Не надо: Первое решение которое приходит в голову, а давайте дождемся конца первой операции, а потом уже счетчик цикла увеличим и в следующей итерации запустим следующую операцию. Но этим Вы просто превращаете асинхронную операцию в синхронную и теряете весь профит от асинхронности. Надо: Понять что основная проблема этого кода не в асинхронности, а в том что Вы не понимаете замыкания. Ну то есть вас не смущает что у вас консольложится json/1_1_1.json столько раз? Проблема два: непонимание областей видимости в js, они же замыкания. Вопрос про потерю значения переменной в цикле - топ 1 вопрос на собеседованиях на мидл разработчика, при неответе на который разговор можно сворачивать. На этом ресурсе этот вопрос в разных формулировках встречается пару раз в неделю. Например вот здесь на него подробно ответили (не упоминая block scope в es6 правда). Ну то есть ядро ошибки в вашем коде выглядит как-то так: for(var i = 0; i<4; i++){ setTimeout(function(){console.log(i)}, 1000) } setTimeout тут - как пример простейшей асинхронной операции. Если Вы не понимаете почему тут выведет 4 раза 4 - надо медитировать пока не поймете. Обязательно. Если вкратце, то это происходит потому что скоуп в js для var переменных - это функция, а не фигурные скобочки. Цикл отдельного скоупа не образует и все четыре раза функция будет ссылаться на одну и ту же переменную i, на одну и ту же область памяти. Ок, я все понял, все равно хочу синхронно. Для того чтобы хитро управлять ходом управления множества асинхронных запросов есть ряд распространенных библиотек, например async, посмотрите например на async.waterfall

Ответ 2



Собери промисы в массив в цикле потом дождись их выполнения используя Promise.all

Комментариев нет:

Отправить комментарий