동기/비동기 & Blocking/Non-Blocking

Dorito·2022년 9월 17일
0

기초공사

목록 보기
1/6

일단 확실하게 알아둬야 할 것은!!!
자바스크립트는 싱글스레드 논블록킹 비동기 동적언어다!


sync: 결과 나오면 바로 씀
async: 결과 나와도 바로 안씀 (항상 Promise 반환)
blocking: 상대가 작업할동안 난 멈춤
non-blocking 상대가 작업할때 나도 계속 작업함
자바스크립트에서 async는 대개 async nonblocking
그래서 둘 개념 헷갈리는 경우가 왕왕 있다.

https://bakjuna.tistory.com/94?category=955668도 같이 읽음

await이 둘 다 뒤에 있을때 순서

(대충 네모로 쳐둔 것은 시간이 지나가고 있다는 뜻..)

순서: hello 0 a 0 c 0 pepe 2000 b 500 test2 500 d 0 test3
-> 모든 값이 나올 때 까지는 약 3000초가 소요된다.

(사이 숫자는 소요 시간, 컴퓨터 연산속도 바로바로 나오는건 임의상 0초라고 함)

await이 result2만 뒤에 붙어 있을 때 순서

순서: hello 0 a 0 c 2000 b 1000 d 0 pepe 2500 test2 0 test3
-> 약 5500초가 소요된다.

이 상태가 Blocking이 되었다는 것!
await 위치 하나가 바뀌었을 뿐인데..!

What the heck is the event loop anyway?

JavaScript programmers like to use words like, “event-loop”, “non-blocking”, “callback”, “asynchronous”, “single-threaded” and “concurrency”.
We say things like “don’t block the event loop”, “make sure your code runs at 60 frames-per-second”, “well of course, it won’t work, that function is an asynchronous callback!”
If you’re anything like me, you nod and agree, as if it’s all obvious, even though you don’t actually know what the words mean; and yet, finding good explanations of how JavaScript actually works isn’t all that easy, so let’s learn!
With some handy visualisations, and fun hacks, let’s get an intuitive understanding of what happens when JavaScript runs.

블로킹에 대한 설명: 7:48

착각하지 말 것: 프로미스는 Async/Await 과는 근본적으로 다르다

https://link.medium.com/UZ5JHbgbotb 여기 글 읽으면 잘 나옴

그 외 참고 문서
https://choar816.tistory.com/190?category=981984
https://velog.io/@yejineee/이벤트-루프와-태스크-큐-마이크로-태스크-매크로-태스크-g6f0joxx

https://link.medium.com/oLafo2Aaotb


예전 필기 내용

예전에 필기해뒀던 내용 여기 글로 합쳤음 (위에 이벤트 루프 영상을 보고나서 다시 읽으니까 더 이해가 잘 됨)

JavaScript 동기/비동기 개념 추가 공부

개인 공부용으로 쓰는 것이고 정확하지 않으니 참고사이트를 위주로 보시길 바랍니다

그림 출처, 참고사이트
https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke
https://ko.javascript.info/event-loop
html 공식 문서에서 이벤트 루프 관련 문서 보기: https://html.spec.whatwg.org/multipage/webappapis.html

비동기의 오해
1. 비동기는 동시의 문제가 아니다! 순서의 문제다

  1. 비동기 promise를 async, await으로 만들었다 -> 완전 틀린말임
    한번 비동기는 영원한 비동기!
setTimeout(() => {
  console.log("a");
}, 0);

setTimeout(() => {
  console.log("b");
}, 1000);

setTimeout(() => {
  console.log("c");
}, 2000);

Promise.resolve().then(() => {
  console.log("p");
});

이 코드를 출력하면 다음과 같이 출력된다

p
a
b
c

이때!!
첫번째 "a" 를 출력하는 SetTimeout 과 "p"를 출력하는 Promise가 맞짱까면 Promise가 이긴다. 왜 그럴까?

왜냐하면 SetTimeout()은 macro 큐에, Promise는 micro 큐에 쌓이는데, micro 큐가 우선순위가 높기 때문이다!
그림에서 직관적으로 보이듯이 모든 함수는 스택이 비워질 때마다 하나씩 쌓여서 실행되고 pop되고 난 후에 또 하나씩 콜스택으로 올라가는데, micro 큐가 먼저 콜 스택으로 올라가서 실행된다.
그런 다음 콜스택과 micro큐가 둘 다 비었을 경우, 이벤트루프는 macro 큐에 task가 있는지 확인하고 실행한다.
마이크로 큐에 해당하는 메서드는 외워두는 것이 좋다.

참고:
마이크로태스크 큐 == 잡 큐
매크로태스크 큐 == 이벤트 큐 or 콜백 큐로 불리기도 한다.

쓸데없는 궁금증:
만약 Macro 큐에 있는 메서드끼리 비교했을 때, 뭐가 먼저인지 알고싶다면.. nodejs 공식문서 를 참고해보자. 근데 어차피 실무에서 setTimeout()이 먼저냐 setImmediate() 이 먼저냐를 굳이 따질 일이 잘 없다고 한다. 최대한 그런 상황을 피하는 것이 좋고 애초에 같은 함수에 집어넣으면 되는 것을 굳이 그런 식으로 설계를 하지 않는다고 한다.
(주의: 정확하지 않다. 한글문서니 당연히 믿거하시겠지만, 내가 이해하기로는 그렇다고 해서 거기까지는 파고가지 않을 생각이다. 게다가 나는 저 부분까지는 아직 이해하기에는 한참 남은 것 같다.)

promise: 실행은 바로 하긴 하는데 결과값을 나중에 내가 원할 때 얻을 수 있는 것

동기/비동기 함수 실행순서 알아보기

예시 1

이 사이트 참고해서 씀

console.log("start");

setTimeout(() => {
  console.log("time out!");
}, 0);

Promise.resolve("Promise!").then((res) => console.log(res));

console.log("End!");
  1. console.log("start"); 출력

  2. setTimeout() 콜스택에서 popped 된 후 WEB API 이동

The setTimeout method is native to the browser: its callback function (() => console.log('In timeout')) will get added to the Web API

-> 시간 완료 이후 macro task에 추가됨

  1. Promise.resolve() 콜스택 추가

    resolve 되고 then의 콜백 함수는 microtask queue에 추가됨

  2. console.log("End!"); 출력

  3. V8 엔진은 콜스택이 비었다는 것을 확인한 후 큐에 뭐가 있는지를 확인한다.

  4. microtask queue 우선 실행 후 마지막으로 macrotask queue 실행


예시 2

이 강의 보면서 필기: https://youtu.be/A7AeQzEoEmc


let a = 2;

const p = new Promise((resolve, reject) => {
 // Promise 내부는 동기 코드임
 console.log("This string comes out first!");
 setTimeout(() => {
   console.log("a");
   let a = 5;
   resolve(a);
 }, 0);
});


console.log("test");

p.then((result) => {
 console.log("result", result);
});
  • 출력
This string comes out first!
test
a
result 5

실행순서

  • 동기
  1. 전역변수 a = 2
  2. promise 내부 함수 호출 ~ 동기방식이므로 console.log("This string comes out first!"); 출력
  3. setTimeout() -> web API로 이동 (백그라운드 개념으로 설명하시는데.. 둘다 사실 명확한 표현인지는 잘 모르겠다.)
  4. console.log("test") 출력
  5. p.then -> web API로 이동
  • 비동기

나머지 Web API로 넘어간 비동기 부분이 남았음!

  1. 0초 지났으니까 조건 만족 -> Macro 큐에 보내줌 -> 동기부분 처리가 끝났으니 setTimeout() 함수를 콜스택 올림
  setTimeout(() => {
    console.log("a");
    let a = 5;
    resolve(a);
  }, 0);
  1. a = 5 재할당, console.log("a") 출력
  2. resolve() 함수 실행 -> p.then 호출 조건 만족!
  3. Web API에 있는 p.then이 Micro 큐로 넘어간다.
p.then((result) => {
  console.log("result", result);
});
  1. 콜스택에 올라간 setTimeout() 함수 완료 후 pop 되고 콜 스택이 비었음!
  2. 큐에 올라가있던 console.log("result", result); 콜스택으로 올라간 후 출력

-> 스택, 큐 모두 텅 비었음! 자바스크립트 종료
(cf. setInterval 같은 경우에는 계속 web api에 남아서 실행됨 ~ clearInterval로 직접 꺼줘야함)

이벤트루프가 왜 있는가? 에 대한 근본적인 해답은 https://youtu.be/0NsJsBdYVHI 에서 타임라인 1:21:13 참고!


자바스크립트 Promises & Async/Await

참고사이트: https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke

자바스크립트에서는 다른 작업에 의존하는 작업을 다룰 때가 있다.
예를들면 사진을 하나 얻고 싶다 할 때
사진을 압축하고 -> 압축된 이미지에 필터를 입히고 -> 그 이미지를 저장한다

이걸 코드로 구현하면 콜백지옥이 펼쳐진다.
http://callbackhell.com/

완전 가독성도 구리고 의존적이라서 별루임
ES6에서 Promise 등장!!

"A promise is a placeholder for a value that can either resolve or reject at some time in the future"


Promise는 status를 갖고있는 객체이다.

쉬운 영어 부분은 그냥 갖구 오고 어려운 부분만... 해석해야지

The value of the PromiseStatus, the state, can be one of three values:

✅ fulfilled: The promise has been resolved. Everything went fine, no errors occurred within the promise 🥳
❌ rejected : The promise has been rejected. Argh, something went wrong..
⏳ pending: The promise has neither resolved nor rejected (yet), the promise is still pending.

this callback function actually receives two arguments.
The value of the first argument, often called resolve or res, is the method to be called when the Promise should resolve.
The value of the second argument, often called reject or rej, is the value method to be called when the Promise should reject, something went wrong.

.then(): Gets called after a promise resolved.
.catch(): Gets called after a promise rejected.
.finally(): Always gets called, whether the promise resolved or rejected.

  • 콜백 극복


코드를

이렇게 극복!!

The result of the .then itself is a promise value. This means that we can chain as many .thens as we want: the result of the previous then callback will be passed as an argument to the next then callback!

https://ko.javascript.info/async-await
https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/async%20&%20performance/README.md#you-dont-know-js-async--performance


return await 안되는 이유

https://eslint.org/docs/latest/rules/no-return-await
https://stackoverflow.com/questions/43353087/are-there-performance-concerns-with-return-await

Difference between return await promise and return promise

https://stackoverflow.com/questions/38708550/difference-between-return-await-promise-and-return-promise

0개의 댓글