promise async await 예제

example.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// promise는 선언과 동시에 실행된다
function asyncFunction(delay, memo) {
console.log(`in`, memo);
const thisIsPromise = new Promise((resolve) => {
setTimeout(() => {
console.log(`done`, memo);
resolve(delay);
}, delay);
});

return thisIsPromise;
}

const fetchData = asyncFunction.bind(null, 1000, 'fetchData');
const fetchHeavyData = asyncFunction.bind(null, 3000, 'fetchHeavyData');

function add(a, b) {
return a + b;
}

async function main() {
console.log(`start main`);
console.time(`main`);

const a = await fetchHeavyData();
const b = await fetchData();

const result = add(a, b);
console.log(`main result`, result);
console.timeEnd(`main`);
}

async function main2() {
console.log(`start main2`);
console.time(`main2`);

const a = fetchHeavyData();
const b = fetchData();

const promiseResultArr = await Promise.all([a, b]);

const result = add(...promiseResultArr);
console.log(`main2 result`, result);
console.timeEnd(`main2`);
}

main();
// main2();
  • 친구한테 비동기에 대해 설명하면서 만든 예제 코드다

설명하기

  • 비동기 초보 친구한테 설명하면서 어떤 부분을 설명했나?

await은 async함수 내부에서 사용 가능

1
2
3
4
5
6
// 1 work
fetchData().then(console.log);

// 2 not work
const data = await fetchData();
console.log(data);
  • 위는 동작하고, 아래는 동작하지 않는다고 해서
  • await 키워드 사용은 async 함수 내부에서만 가능하다고 말했다

express에서 비동기

1
2
3
4
5
6
7
8
9
10
11
12
13
// 1 work...
router.get('/async', function (req, res) {
asyncFunction(1000, 'memo').then((result) => {
res.json(result);
});
});

// 2 work
router.get('/async', async function (req, res) {
const result = await asyncFunction(1000, 'memo');

res.json(result);
});
  • 1로 했을 때 어떻게 동작하냐 해서
  • 2로 하는 게 좋을 것 같다고 했다
  • 실제로 테스트 결과 1, 2 모두 잘 동작했다
  • 1의 경우 안될 줄 알았는데 express가 똑똑한 건가…

then 콜백 함수에서 return

1
2
3
4
5
6
7
8
9
10
11
12
13
// 1 not work
async function f() {
await asyncFunction(1000, 'memo').then((result) => {
return result;
});
}

// 2 work
async function f() {
return await asyncFunction(1000, 'memo').then((result) => {
return result;
});
}
  • 1의 경우에서 함수 f의 반환 값이 없다 하여,
  • then 콜백 함수에서 return이 함수 f의 리턴을 의미하지 않는다고 말했다

체이닝

1
2
3
4
5
6
7
const a = await asyncFunction(1000, 'memo') // 1000
.then((result) => {
return result * 2;
}) // 2000
.then((result) => {
return result + 200;
}); // 2200
  • then을 끝까지 수행한 후에 결괏값을 반환한다고 알려줬다
  • a에는 2200이 들어간다

생략 표현

1
2
3
4
5
6
function add1(a) {
return a + 1;
}

asyncFunction(1000, 'memo').then(add1);
asyncFunction(1000, 'memo').then((res) => add1(res));
  • 인자를 그대로 다른 함수에 넘겨주는 경우 생략이 가능하다

Promise.all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1
{
const a = await fetchHeavyData();
const b = await fetchData();
const result = add(a, b);
}

// 2
{
const a = fetchHeavyData();
const b = fetchData();

const promiseResultArr = await Promise.all([a, b]);

const result = add(...promiseResultArr);
}
  • promise는 선언과 동시에 실행된다
  • 1의 경우 서로 무관한 비동기 흐름을 순차적으로 처리한다
  • 2의 경우 Promise.all을 사용해 병렬 처리하여 1보다 효율적이다

jest 'describe' is not defined

.eslintrc.json
1
2
3
4
5
6
7
{
"env": {
// ...
"jest": true
}
// ...
}
  • env에 추가해준다
  • ⚠️ 추가 해주고 F1 > eslint.restart 명령을 실행해 eslint를 재부팅한다

상황

  • jest를 eslint와 처음 사용해본다

eslint(no-undef)

‘describe’ is not defined.
‘test’ is not defined.
‘expect’ is not defined.

  • 위 에러가 나왔다

참고

parseInt vs. Math.floor

1
2
3
4
5
6
7
8
9
10
11
let n = 1.1;
console.log(Math.floor(n), parseInt(n)); // 1 1

n = 1.9;
console.log(Math.floor(n), parseInt(n)); // 1 1

n = -1.1;
console.log(Math.floor(n), parseInt(n)); // -2 -1

n = -1.9;
console.log(Math.floor(n), parseInt(n)); // -2 -1
  • 소수점 이하를 버릴 때 Math.floor()(내림)를 주로 썼는데 음수에서 기대한 것과 다르게 동작하는 것을 알게 되었다…
  • parseInt()는 매개변수로 문자열을 받기 때문에 주로 문자열로부터 숫자를 파싱 할 때 사용하는 줄 알았다
  • parseInt()는 매개변수로 string 타입이 아니면 스트링으로 자동 캐스팅하여 그냥 숫자를 넘겨줘도 잘 동작한다
  • ⚠️ 음 근데 문자열로 된 숫자를 인풋으로 줄 때 주의해야 한다
  • parseInt나 Math.floor나 상관없이 엄청나게 큰 문자열로 된 숫자를 number 타입으로 캐스팅하는 용도로 사용하면 안 된다

참고

js scope 스코프, 렉시컬 스코프

  • 전역 변수, 지역 변수 구분하는 것이다
  • 스코프 ; 중괄호로 묶인 영역

전역, 지역 변수

지역변수는 바깥에서 사용할 수 없다
1
2
3
4
5
6
7
const f = () => {
const x = 123;
console.log(x);
};

f(); // 123
console.log(x); // 에러 ; ReferenceError: x is not defined
  • 함수 f 에 선언된 f의 지역변수 x를 바깥에서 사용하지 못하는 모습이다
전역 변수 사용
1
2
3
4
5
6
7
8
const x = 123;

const f = () => {
console.log(x);
};

console.log(x); // 123
f(); // 123
  • 반대로 함수에서는 바깥에 선언된 전역 변수 x를 사용할 수 있다
같은 변수명 일 때는 지역변수
1
2
3
4
5
6
7
8
9
const x = 123;

const f = () => {
const x = 999;
console.log(x);
};

console.log(x); // 123
f(); // 999
  • 전역 변수, 지역 변수에 동일한 이름의 변수가 있으면 가까운 변수를 참조한다

스코프 체인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let x = 1;

{
console.log(x); // 1

{
x = 2;
console.log(x); // 2

{
let x = 111;
console.log(x); // 111

{
console.log(x); // 111
x = 222;
console.log(x); // 222
}
}
}
}
  • 가깝다는 것은 일단 {} 스코프를 기준으로
  • 일단 자기 자신이 속한 스코프에서 변수 x를 찾는다
  • 없으면 상위 스코프에서 찾는다
  • 찾을 때까지 반복한다
  • 이를 스코프 체인이라고 한다
  • 근데 이 스코프에 의한 전역, 지역 변수 구분은,
  • 그러니까 내가 진짜로 참조하는 변수는 스코프를 선언할 때 결정된다 (렉시컬 스코핑)

렉시컬 스코핑

렉시컬 스코핑
1
2
3
4
5
6
7
8
9
10
11
12
13
let x = 1;

const f = () => {
console.log(x);
};

const main = () => {
let x = 999;
f(); // 1
console.log(x); // 999
};

main();
  • 선언할 때 결정된다는 것은
  • 위 코드의 결과처럼
  • 함수 f 가 선언 당시에 최상위 스코프의 let x = 1을 바라본다는 것이다
  • 함수 main을 보면 함수 f가 실행되기 전에 스코프의 let x = 999는 함수 f에 영향을 주지 않는다
  • 이런 개념들은 js를 하면서 그냥 익숙해져 있어서 용어로 설명할 수 있을 정도는 아니었다
  • 이번 기회에 js 개념, 용어들을 정리해봐야겠다

참고

js !! not not (double not)

  • 어느 날 조건문에서 !!을 본 적이 있다
  • 처음에는 왜 쓸모없이 not을 2번 썼지?하면서 리팩토링이랍시고 !!을 지워버렸다 ㅋㅋㅋ
  • 그런데 알아보니까 Boolean 타입으로 캐스팅해주는 것이었다
1
2
3
if (!obj) return; // obj가 존재하면 다음 로직 진행

// obj ...
  • 나는 보통 obj가 유효한지 체크하려고 !을 붙여서 사용하곤 했는데
  • 이미 !한 개를 붙이면 자동으로 Boolean으로 캐스팅된다
  • 거기에 한 번 더 not을 하면 not not 이라서 Boolean(obj)와 똑같아진다는 것을 알 수 있다

참고

array to hashmap js

ex1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const arr = [
{ id: 1, value: 1 },
{ id: 2, value: 2 },
{ id: 3, value: 3 },
];

let map = new Map();

arr.reduce((newMap, cur) => {
const { id, value } = cur;
newMap.set(id, value);
return newMap;
}, map);

console.log(map);
map.get(1);
  • reduce로 합쳐주는 방식으로 할 수 있고
ex2
1
2
3
4
5
6
7
8
9
const arr = [
{ id: 1, value: 1 },
{ id: 2, value: 2 },
{ id: 3, value: 3 },
];

const map = new Map(arr.map((item) => [item.id, item.value]));

console.log(map);
  • array.map()과 Map 생성자로 짧고 이쁘게 변화해줄 수 있다

참고

setTimeout Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const delay = (ms) => {
return new Promise((resolve) =>
setTimeout(() => {
resolve(ms);
}, ms)
);
};
const main = async () => {
console.log(`main start`);

const result = delay(1000);
result.then(console.log);
console.log(`main end`);
};

const main2 = async () => {
console.log(`main start`);

const result = await delay(1000);
console.log(result);
console.log(`main end`);
};

main();
// main2();
  • promise를 알려주는 여러 글, 영상에서 setTimeout으로 예제로 진행한다
  • setTimeout을 promise로 감싸면 딜레이를 만들 수 있다
  • promise가 처음에 엄청 헷갈리는데, 막힐 때마다 이 예제를 먼저 작성해보고 적용해보았던 것 같다

참고

string to number 요상한 문법 (js)

  • string type 변수앞에 + 기호를 넣으면 number type 으로 캐스팅된다…
  • 이런 문법은 wow…
example.ts
1
2
3
4
5
6
7
function squareValue(x: any) {
return Math.pow(x * 1, 2);
}

function squareValue(x: string | number) {
return Math.pow(+x, 2);
}
  • any 사용을 피하라는 예제를 코드를 보는데 우연히 알게되었다

참고