[Javascript](EN) async await is not work sequentially in forEach
When I use async await in javascript, there was problem that results were not in sequential order. Find out reason and solution for it.
Environment and Prerequisite
- Javascript
- async and await
- Python
- Flask
Before In
- Briefly explain about async await and test environment.
async await
async
- Asynchronous function define keyword
- Add it to use below
await
.
await
- It waits
Promise
object and can be used inasync
function. - Wait until
Promise
becomesresolve
orreject
. - In simple terms, it is a keyword which waits until response of asynchronous request comes.
Examples
function timer(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function test() {
var x = await timer(10); // wait 2000 milliseconds
console.log(x); // 10
}
test();
Test Environment
- Send client’s id and server returns it
- Server is written in python but there is no problem with understanding because we only focus on client code.
Test Structure
Server Code
from flask import Flask, request
import json
import time
app = Flask(__name__)
host_addr = "0.0.0.0"
port_num = "8080"
@app.route("/test", methods = ['POST'])
def test():
print("Time:" + str(int(round(time.time() * 1000))) + ", reqeust from client : " + str(request.json['data']))
return str(request.json['data'])
if __name__ == "__main__":
app.run(host = host_addr, port = port_num)
Issue
- Response does not come sequentially when we use
forEach
..
Request Code
arr = [1, 2, 3, 4];
arr.forEach(async (i) => {
const response = await axios.post("http://0.0.0.0:8080/test", {
data: i,
});
console.log("forEachRequestUsingAsyncAwait server response: " + response.data);
});
Response
- Responses are not sequential.
$ node test.js
forEachRequestUsingAsyncAwait server response: 1
forEachRequestUsingAsyncAwait server response: 4
forEachRequestUsingAsyncAwait server response: 3
forEachRequestUsingAsyncAwait server response: 2
Server Log
- Requests are not sequential.
Time:1602409922301, reqeust from client : 1
127.0.0.1 - - [11/Oct/2020 18:52:02] "POST /test HTTP/1.1" 200 -
Time:1602409922302, reqeust from client : 4
127.0.0.1 - - [11/Oct/2020 18:52:02] "POST /test HTTP/1.1" 200 -
Time:1602409922303, reqeust from client : 3
127.0.0.1 - - [11/Oct/2020 18:52:02] "POST /test HTTP/1.1" 200 -
Time:1602409922303, reqeust from client : 2
127.0.0.1 - - [11/Oct/2020 18:52:02] "POST /test HTTP/1.1" 200 -
Reason
Reason
- forEach calls a provided callback function once for each element in an array in ascending order. Callback runs in sequential but it does not wait until one is finished.
- That is because it runs not like iteration which runs next step when one step is finished. It runs each callback.
Documents
Array.prototype.forEach ( callbackfn [ , thisArg ] )
- According to ECMAScript Language Specification, it says “
callbackfn
should be a function that accepts three arguments.forEach
callscallbackfn
once for each element present in the array, in ascending order.callbackfn
is called only for elements of the array which actually exist; it is not called for missing elements of the array.”
arr.forEach(callback(currentValue[, index[, array]]) {
// execute something
}[, thisArg]);
- According to MDN Web Docs, it says “
forEach()
calls a providedcallback
function once for each element in an array in ascending order.”
Solutions
Use for-in or for-of
for-in
- Request code
async function forInRequest () {
for (const i in arr){
const response = await axios.post("http://0.0.0.0:8080/test", {
data: arr[i],
});
console.log("forInRequest server: " + response.data);
}
}
forInRequest();
- Response result
node test.js
forInRequest server: 1
forInRequest server: 2
forInRequest server: 3
forInRequest server: 4
- Server response result
Time:1603631271374, reqeust from client : 1
127.0.0.1 - - [25/Oct/2020 22:07:51] "POST /test HTTP/1.1" 200 -
Time:1603631271383, reqeust from client : 2
127.0.0.1 - - [25/Oct/2020 22:07:51] "POST /test HTTP/1.1" 200 -
Time:1603631271386, reqeust from client : 3
127.0.0.1 - - [25/Oct/2020 22:07:51] "POST /test HTTP/1.1" 200 -
Time:1603631271388, reqeust from client : 4
127.0.0.1 - - [25/Oct/2020 22:07:51] "POST /test HTTP/1.1" 200 -
for-of
- Request code
async function forOfRequest () {
for (const i of arr){
const response = await axios.post("http://0.0.0.0:8080/test", {
data: i,
});
console.log("forOfRequest server: " + response.data);
}
}
forOfRequest();
- Response result
node test.js
forOfRequest server: 1
forOfRequest server: 2
forOfRequest server: 3
forOfRequest server: 4
- Server response result
Time:1603631202328, reqeust from client : 1
127.0.0.1 - - [25/Oct/2020 22:06:42] "POST /test HTTP/1.1" 200 -
Time:1603631202335, reqeust from client : 2
127.0.0.1 - - [25/Oct/2020 22:06:42] "POST /test HTTP/1.1" 200 -
Time:1603631202338, reqeust from client : 3
127.0.0.1 - - [25/Oct/2020 22:06:42] "POST /test HTTP/1.1" 200 -
Time:1603631202342, reqeust from client : 4
127.0.0.1 - - [25/Oct/2020 22:06:42] "POST /test HTTP/1.1" 200 -
Reference
- https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
- https://tc39.es/ecma262/#sec-array.prototype.foreach
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
- https://tc39.es/ecma262/#sec-for-in-and-for-of-statements