타임라인 사이에 자원 공유하기
- 16.1 자원 공유 문제
- 16.2 큐 만들기
- 16.3 큐를 사용해서 순서 보장하기
- 16.4 큐를 재사용 가능하게 만들기
- 16.5 큐의 한계와 활용
큐는 결국 **"여러 타임라인을 하나의 타임라인으로 만드는 도구"**야.
15장에서 전역변수를 지역변수/인자로 바꿔서 공유 자원을 줄였잖아. 하지만 어떤 자원은 공유를 피할 수 없어 — DOM, 데이터베이스, 외부 API 같은 것. MegaMart의 장바구니 버그를 다시 보면, 15장에서 전역변수를 많이 줄였지만 DOM 업데이트는 여전히 공유 자원이야. "장바구니에 담기"를 빠르게 두 번 클릭하면, 두 번의 DOM 업데이트가 순서 없이 일어날 수 있어. 핵심 문제는 두 타임라인의 액션이 올바른 순서로 실행되어야 하는데, 순서가 보장되지 않는다는 거야.
해결법은 **큐(queue)**를 만들어서 작업을 순서대로 처리하는 거야. 큐의 아이디어는 간단해 — 작업이 들어오면 큐에 넣고, 큐에서 하나씩 꺼내서 실행하고, 현재 작업이 끝나야 다음 작업을 시작하지.
function Queue() {
var queue_items = [];
var working = false;
function runNext() {
if (working) return;
if (queue_items.length === 0) return;
working = true;
var item = queue_items.shift();
item(function done() {
working = false;
runNext();
});
}
return function(work) {
queue_items.push(work);
runNext();
};
}
이 큐는 한 번에 하나의 작업만 실행해. 작업이 done() 콜백을 호출해야 다음 작업이 시작되거든.
MegaMart에 큐를 적용하면:
var update_cart_queue = Queue();
function add_item_to_cart(item) {
update_cart_queue(function(done) {
cart = add_item(cart, item);
var total = calc_total(cart);
update_total_dom(total);
done(); // 이 작업 끝남, 다음 작업 시작해도 됨
});
}
이제 "장바구니에 담기"를 아무리 빠르게 눌러도, 각 클릭의 처리가 순서대로 실행돼. 첫 번째 클릭이 done()을 호출해야 두 번째 클릭의 처리가 시작되거든. 두 타임라인이 겹치지 않아.
큐를 좀 더 재사용하기 좋게 만들 수도 있어. **드롭 큐(dropping queue)**는 빠르게 여러 번 들어온 작업 중 마지막 것만 실행하는 변형이야. 검색 자동완성 같은 데 유용하지 — 사용자가 "abc"를 타이핑하면 "a", "ab", "abc" 세 번의 요청이 생기는데, "a"와 "ab"는 필요 없으니 버려도 되거든. 큐의 종류를 상황에 맞게 고를 수 있어 — 일반 큐는 모든 작업을 순서대로 처리하고, 드롭 큐는 최신 작업만 처리하고 오래된 건 버리고, 배치 큐는 일정 개수나 시간만큼 모아서 한번에 처리하지.
큐가 적합한 경우는 순서가 중요한 작업, 같은 자원에 대한 동시 접근을 직렬화하고 싶을 때, 비동기 작업의 순서를 보장하고 싶을 때야. 부적합한 경우는 작업들이 서로 독립적이라 병렬로 처리해도 되는 경우(큐를 쓰면 불필요하게 느려짐)나, 작업의 결과를 바로 받아야 하는 경우지.
정리
16장 읽고 기억할 거 세 가지:
- 공유 자원이 피할 수 없으면 큐로 순서를 보장하라. 큐 = 여러 타임라인을 하나로 직렬화
- 큐의 핵심은 done() 콜백. 현재 작업이 done()을 호출해야 다음 작업이 시작된다
- 드롭 큐 같은 변형으로 상황에 맞게 조절 가능. 모든 작업을 처리할 필요가 없으면 오래된 걸 버릴 수도 있다