forEach에서 async await를 사용할 때 결과값이 순차적으로 오지 않는 상황이 발생했는데 이에 대한 해결법과 원인에 대해서 알아보자


환경

  • Javascript
  • async and await
  • Python
  • Flask


들어가기에 앞서

  • 간단하게 async await에 대해 정리하고 테스트할 환경을 정의한다.

async await

async

  • 비동기 함수를 정의하는 키워드
  • 하단의 await를 사용하기 위해 붙여줘야합니다.

await

  • Promise 객체를 기다리며 async 함수안에서 사용 가능합니다.
  • Promiseresolve되거나 reject될 때까지 기다립니다.
  • 쉽게 설명해 비동기 요청에 대한 응답이 올때까지 기다리는 키워드라고 보시면 됩니다.

예제

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


테스트 환경

  • 클라이언트에서 본인의 ID를 보내서 그 ID값을 그대로 반환하게 환경을 구축
  • 클라이언트는 자바스크립트로 서버는 파이썬으로 했으나 테스트 용도이기에 서버측은 몰라도 이해에 문제는 없습니다.

테스트 구조

서버 코드

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)


문제

  • forEach를 사용시 순차적으로 요청 응답이 오지 않는다.

요청 코드

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

응답

  • 응답이 순서대로 오지 않았다.
$ node test.js
forEachRequestUsingAsyncAwait server response: 1
forEachRequestUsingAsyncAwait server response: 4
forEachRequestUsingAsyncAwait server response: 3
forEachRequestUsingAsyncAwait server response: 2

서버측 로그

  • 순서대로 요청이 오지 않았다.
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 -


이유

이유

  • forEach는 배열에 대해 Callback을 순차적으로 실행하기 때문이다. Callback을 순차적으로 실행하지만 Callback이 하나가 끝나기까지 기다렸다가 다음이 실행되는게 아니기 때문이다.
  • 반복문처럼 하나가 완료되면 다음 순서에 따라 진행되는게 아니라 각각을 콜백으로 실행하기 때문이다.

공식문서 자료

Array.prototype.forEach ( callbackfn [ , thisArg ] )
  • ECMAScript Language Specification에 보면 forEach는 “callbackfn should be a function that accepts three arguments. forEach calls callbackfn 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]);
  • MDN Web Docs에 보면 forEach는 “forEach() calls a provided callback function once for each element in an array in ascending order.”라고 나와있다.


해결방법

for-in 혹은 for-of 사용

for-in

  • 요청 코드
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();
  • 응답 결과
node test.js
forInRequest server: 1
forInRequest server: 2
forInRequest server: 3
forInRequest server: 4
  • 서버 응답 결과
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

  • 요청 코드
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();
  • 응답 결과
node test.js
forOfRequest server: 1
forOfRequest server: 2
forOfRequest server: 3
forOfRequest server: 4
  • 서버 응답 결과
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 -


참고자료

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 in async function.
  • Wait until Promise becomes resolve or reject.
  • 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 calls callbackfn 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 provided callback 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

“SyntaxError: Non-ASCII character …, but no encoding declared”와 같은 이슈가 생겼을 때 해결 방법을 알아보자


환경

  • Python


문제와 해결 방법

문제 상황

  • 파이썬 코드를 실행했는데 아래와 같은 오류를 보여준다.
python /home/twpower/MailReminder/reminder.py
...
SyntaxError: Non-ASCII character '\xeb' in file /home/twpower/MailReminder/reminder.py on line 12, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details


원인

  • 실행중인 코드에 ASCII가 아닌 문자가 들어있는데 인코딩을 명시해주지 않아서 생기는 오류


해결 방법

  • 파이썬 파일 제일 상단에 # -*- coding: utf-8 -*-를 추가한다.
# -*- coding: utf-8 -*-
...


참고자료

Solution for “SyntaxError: Non-ASCII character …, but no encoding declared”


Environment and Prerequisite

  • Python


Problem and Solution

Issue

  • Error comes out like below when running python code.
python /home/twpower/MailReminder/reminder.py
...
SyntaxError: Non-ASCII character '\xeb' in file /home/twpower/MailReminder/reminder.py on line 12, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details


Cause

  • Issue which there was non ascii character in running code but encoding was not declared


Solution

  • Add # -*- coding: utf-8 -*- to top of python code file.
# -*- coding: utf-8 -*-
...


Reference

undefined와 null의 차이를 알아보자


환경

  • Javascript


undefined와 null

undefined

  • 변수가 값을 할당받지 않았을 때 사용되는 원시 값(primitive value)입니다.
  • 함수의 반환이 명시적으로 없으면 undefined를 반환합니다.
  • 원시 자료형 중 하나
// test default
let test;
console.log('variable test is ' + test);

// if statement
if (test === undefined) {
  console.log('variable test is ' + test);
}

// if statement
if (undefined) {
  console.log('undefined is considered as true');
} else {
  console.log('undefined is considered as false');
}

// type test in if statement
// return type of typeof function is string
if (typeof(test) === 'undefined') {
  console.log('variable test value is ' + test + ' and type is ' + typeof(test));
}

// function return
function testReturnNothing() {
  let c;
}
console.log('testReturnNothing returns ' + testReturnNothing());

// check which is not defined
if (typeof(notDefinedVariable) === 'undefined') {
  console.log('variable notDefinedVariable is not defined');
}
variable test is undefined
variable test is undefined
undefined is considered as false
variable test value is undefined and type is undefined
testReturnNothing returns undefined
variable notDefinedVariable is not defined


null

  • 의도적으로 객체나 변수가 비어있음을 나타내는 원시 값입니다.
  • API에서는 대개 객체를 기대했지만, 어떤 적합한 객체도 존재하지 않을 때 반환합니다.
  • 불리언(Boolean) 자료형에서는 거짓을 의미합니다.
// test default
let test = null;
console.log('variable test is ' + test);

// if statement
if (test === null) {
  console.log('variable test is ' + test);
}

// if statement
if (null) {
  console.log('null is considered as true');
} else {
  console.log('null is considered as false');
}

// type test in if statement
// return type of typeof function is string
// its return type is not 'null' it is 'object'
if (typeof(test) === 'object') {
  console.log('variable test value is ' + test + ' and type is ' + typeof(test));
}

// function return
function testReturnNull() {
  return null;
}
console.log('testReturnNull returns ' + testReturnNull());
variable test is null
variable test is null
null is considered as false
variable test value is null and type is object
testReturnNull returns null


비교

undefined

  • 아직 할당되지 않았음을 의미하는 값

null

  • 의도적으로 비어있음을 나타내는 값

기타

  • ==를 사용시 둘은 같으며 ===를 사용시 둘은 다릅니다.
  • ===의 경우에 type까지 같이 비교하기 때문에 그렇습니다. 각각의 type은 아래처럼 다릅니다.
typeof(null)         // object
typeof(undefined)    // undefined

null === undefined   // false
null == undefined    // true

!null                // true
!undefined           // true


참고자료