Task: I collect geodata data from the server (not mine). To do this, I form a request for a specific interval of coordinates and send a request to the server in the for loop. I write down the answer to a file and so that the server does not ban it, changing the ip address via tor-control Awaiting response is implemented using promise. This is the ideal. In fact (judging by the logs in the console), the data is sent as horrible. Tell me where I turned the wrong way.

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'); }) } } 

Judging by the logs, it turns out that he sweeps through the loop 4 times without waiting for the previous operations.

Console Log Text:

 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 

Called methods:

 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); } }); }); } 

I am just starting to get acquainted with js, so if, besides the answer, you can also advise where and how best to familiarize myself with it.

  • I can look past something, but it seems clear that the cycle is a cycle, and the request to the server inside it is executed, and the requests are executed asynchronously, that is, the request is gone and not waiting for an answer, the cycle has driven further. I don’t even know how to cram in here, there’s probably an option through the closure, and with each successful request the flag is incremented by one and when a new start is checked whether the flag is too big and if it is exceeded then quit the function ... - kils

2 answers 2

In the code for this, there are two main problems (out of three) of javascript beginner developers I see.

Problem one - misunderstanding of asynchrony.
Of course, the loop does not wait for the result of the asynchronous operation to return in order to scroll further.

Imagine that you have four hyperactive couriers, and you throw them four balls in a row. In what order will they bring them back? Hell knows.

And the fact that inside Promise.then, this is a Kolbek, only written more conveniently.

What to do?
Do not:
The first decision that comes to mind, let's wait for the end of the first operation, and then we will increase the cycle counter and in the next iteration we will launch the next operation. But with this you simply turn an asynchronous operation into a synchronous operation and lose all the profit from asynchrony.
It is necessary:
Understand that the main problem of this code is not asynchronous, but that you do not understand the closure. Well, that is, you are not confused by the fact that you have json/1_1_1.json so many times?

Problem two:
lack of understanding of scopes in js, they are also closures. The question about the loss of the variable value in the cycle is the top 1 question during interviews for the developer's middle, if you don’t answer the question, you can minimize the conversation. On this resource, this question in different formulations occurs a couple of times a week. For example , here it was answered in detail (not to mention the block scope in es6 true).

Well, that is, the core of the error in your code looks something like this:

 for(var i = 0; i<4; i++){ setTimeout(function(){console.log(i)}, 1000) } 

setTimeout here - as an example of the simplest asynchronous operation. If you do not understand why it will display 4 times 4 times - you need to meditate until you understand. Required.
In short, this is because the skoup in js for var variables is a function, not curly brackets. The cycle of a separate scop does not form and all four times the function will refer to the same variable i, to the same memory area.

Ok, I understood everything, I still want to synchronously.
In order to cunningly manage the progress of managing a set of asynchronous requests, there are a number of common libraries, for example async , for example, look at async.waterfall

  • Detailed response. Thank you very much, understanding has really increased (especially about skoupov), after the development of single-player games, it is difficult to switch to the web and even to another language. The only thing I wanted to ask was, what if synchrony is REALLY necessary for me? I understand that this is not about node'js then, its asynchronous chip, but what would you advise? After each successful request, I need to change the ip in order to return normal data and not an array with the text 'spam'. In the case of asynchrony, I can return 4 requests at times. How should I be then? - justyx
  • @justyx, I’m a front and not a nodist, so I’m not sure one hundred percent how to do it now, but it seems with the help of this: caolan.imtqy.com/async - Duck Learns to Take Cover
  • one
    @justyx last_request = last_request.then(() => GetData()) and so on. - Pavel Mayorov

Collect promises in an array in a loop, then wait until they are executed using Promise.all

  • 2
    then the iterations will remain independent and their execution order will not be guaranteed - Grundy
  • one
    @Grundy from the question, I did not understand that the order of re-promise of promises is important. If important, you can use Promise.mapSeries (from bluebird), npmjs.com/package/promise-series or an alternative from the library that is used. The basic technique remains the same, only the method changes - pitersky