카페에서 커피를 주문하려고 줄을 서는 모습을 상상해 보자.
카페의 사정상, 커피를 주문한 먼저 온 김코딩이 주문한 커피를 받을 때까지, 줄 서 있는 박해커가 주문조차 할 수 없다고 하자. 이를 blocking
이라고 부른다. 하나의 작업이 끝날 때까지, 이어지는 작업을 막는 것이다.
박해커는 김코딩이 주문한 커피가 나오고 나서야 커피를 주문할 수 있다. 김코딩의 커피 주문 완료 시점과 박해커의 커피 주문 시작 시점이 같다. 이렇게 시작 시점과 완료 시점이 같은 상황을 동기적synchronous
라고 한다.
효율적인 카페 운영을 위해서 아래와 같이 카페의 주문 과정을 변경해 보자.
blocking
되지 않고, 언제든지 주문을 받을 수 있다.Node.js를 만든 개발자도 위 대안이 합리적이라고 생각했다. 그래서 Node.js를 non-blocking
하고 비동기적asynchronous
로 작동하는 런타임으로 개발하게 된다.
JS의 비동기적 실행Asynchronous execution
이라는 개념은 웹 개발에서 특히 유용하다. 특히 아래 작업은 비동기적으로 작동되어야 효율적이다.
Asynchronous call
callback review
- 다른 함수(A)의 전달인자
argument
로 넘겨주는 함수 (B)
- parameter를 넘겨받는 함수 (A)는 callback함수(B)를 필요에 따라 즉시 실행
synchronously
할 수도 있고, 아니면 나중에asynhronously
실행할 수도 있다.
function B(){
console.log('called at the back!');
}
function A(callback){
callback(); // callback === B
}
A(B);
callback in action : 반복 실행하는 함수
iterator
[1,2,3].map(function(ele,idx){ //3회 반복 = idx만큼 반복 return ele*ele; });
callback in action : 이벤트에 따른 함수
event handler
document.querySelector('#btn').addEventListner('click', function(e){ console.log('button clicked'); });
헷갈리지 말자!
function handleClick(){
console.log('button clicked');
};
documnet.querySelector('#btn').onclick = handleClick; //OK
documnet.querySelector('#btn').onclick =function(){
handleClick();
} //OK
documnet.querySelector('#btn').onclick =handleClick.bind(); // OK
documnet.querySelector('#btn').onclick =handleClick();
//NO --> 함수 실행을 연결하는 것이 아니다. 함수 자체를 연결하는 것이다.
//handleClick() 은 return 값이 없기 때문에 결과 값은 undefined가 나온다.
blocking vs. non-blocking
전화 | 문자 |
---|---|
하던일을 멈추고 밥다야 한다. (blocking) | 확인 후, 나중에 답장할 수 있다. (non-blocking) |
요청에 대한 결과가 동시에 일어난다. (synchronous) | 요청에 대한 결과가 동시에 일어나지 않는다. (asynchronous) |
커피 주문으로 알아보는 동기 vs. 비동기
동기 : 요청에 대한 결과가 동시에 일어난다.
만일 커피 주문이 동기적으로 작동한다면?
- 손님 1이 아메리카노를 주문한다.
- 접수를 받은 직원이 아메리카노를 내린다.
- 직원이 손님 1에게 아메리카노를 전달한다.
- 손님 2가 카페라떼를 주문한다.
- 접수를 받은 직원이 카페라떼를 만든다.
- 직원이 손님 2에게 카페라떼를 전달한다.
이 중에서 잘 못된 것은?
왜 따로 주문받고 만드는가 --> 동시에 주문 받고 순차적으로 뽑으면 되는 거 아닌가?
--> 손님 2가 카페라떼를 주문한다.
: 손님2는 손님1이 아메리카노를 전달 받을때 까지 주문도 하지 못하고 대기열에 머물러 있어야 한다.
비동기
- 손님 1이 아메리카노를 주문한다.
- 접수를 받은 직원이 아메리카노를 내린다.
- 아메리카노가 완성되면 직원이 손님 1을 부른다.
- 아메리카노를 손님 1에게 전달한다.
- 손님2가 카페라떼를 주문한다.
- 접수를 받은 직원이 카페라떼를 만든다.
- 카페라떼가 완성되면 직원이 손님 2를 부른다
- 카페라떼를 손님 2에게 전달한다.
완성되면 <-- callback
function waitSync(ms) {
let start = Date.now();
let now = start;
while (now - start < ms) {
now = Date.now();
}
}
// 현재 시작과 시작 시각을 비교하며 ms 범위 내에서 무한 루프를 도는 blocking 함수이다.
function drink(person, coffee) {
console.log(person + "는" + coffee + "를 마십니다");
}
function orderCoffeeSync(coffee) {
console.log(coffee + "가 접수되었습니다.");
waitSync(2000); //<-- 2sec pause
return coffee;
}
let customers = [
{
name: "Steve",
request: "cafe latte",
},
{
name: "John",
request: "Americano",
},
];
//call synchronously
customers.forEach(function (customers) {
let coffee = orderCoffeeSync(customer.request);
drink(customer.name, coffee);
});
//cafe latte 가 접수되었습니다.
//Steve는 cafe latte를 마십니다.
//Americano가 접수되었습니다.
//John는 아메리카노를 마십니다.
function waitSync(callback, ms) {
setTimeout(callback, ms);
//특정 시간 이후에 callback 함수가 실행되게끔 하는 브라우저 내장 기능.
}
function drink(person, coffee) {
console.log(person + "는" + coffee + "를 마십니다");
}
let customers = [
{
name: "Steve",
request: "cafe latte",
},
{
name: "John",
request: "Americano",
},
];
function orderCoffeeASync(menu, callback) {
console.log(menu + "가 접수되었습니다");
waitASync(function () {
callback(menu);
}, 2000);
}
//call asynchronously
customers.forEach(function (customers) {
orderCoffeeASync(customer.request, function (coffee) {
drink(customer.name, coffee);
});
});
//caffe latte가 접수되었습니다.
//Americano 가 접수되었습니다.
//Steve는 caffe latte를 마십니다.
//John는 Americano를 마십니다.
비동기 함수 전달 패턴 1 : callback 패턴
let request = 'caffelatte';
orderCoffeeAsync(request, function(response){
//respnse -> 주문한 커피 결과
drink(response);
});
비동기 함수 전달 패턴 2 : 이벤트 등록 패턴
let request = 'caffelatte';
orderCoffeeAsync(request).onready=function(response){
//respnse -> 주문한 커피 결과
drink(response);
};
브라우저의 비동기 함수 작동 원리를 알려면 (advanced)
Event Loop
Philip Roberts: Help, I'm stuck in an event-loop