Задача: собираю данные геодаты с сервера (не моего). Для этого формирую запрос для определенного промежутка координат и в цикле 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'ом знакомство только начинаю, поэтому если кроме ответа еще и посоветуете где и как лучше ознакамливаться буду только рад.
В коде этом сразу две основных проблемы (из трех) начинающих 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
Собери промисы в массив в цикле потом дождись их выполнения используя Promise.all
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
В разработке сайта, начинающийНе могу подцепить javascript к шаблону сайта
Разместил вот здесь всю реализациюВ каком направлении думать, чтобы реализовать раскрытие нового блока как-бы из квадрата, который расположен...
У меня есть список , в котором будет неизвестное количество 'li'В каждой li будут всегда одинаковые элементы,которые должны редактироваться...
Всем приветДопустим у нас есть массив элементов со свойствами name и ID