AsyncContext
- 비동기 작업 전체에 걸쳐 맥락(context)을 유지할 수 있게 해주는 JavaScript 표준 제안 (TC39 Stage 2)
왜 필요할까? - 문제 1. 비동기에서의 맥락 상실
- 쉽게 말해서 실행컨텍스트의 비동기버전이라고 생각하면 됨.
- 전역 변수 등을 사용하여 비동기 함수 내에서 클로저로 접근/수정 가능한 경우에 비동기 함수 호출에 따라서 결과값이 달라질 수 있고 이걸 호출처에서 제어할 방법이 없음.
- 즉, 비동기 작업에서 전역상태 없이도 누가 호출했는지?를 기억하고, 격리할 수 있게 해주는 것
// 동기 코드 - 문제 없음
function processUser() {
const userId = getCurrentUser(); // hope
logAction(); // hope 이 무언가를 함
saveData(); // hope의 데이터 저장
}
// 비동기 코드
async function processUser() {
const userId = getCurrentUser(); // hope
await delay(100);
logAction(); // 누가 한 건지 모름
saveData(); // 누구 데이터인지 모름!
}
왜 사라질까?
let currentUser = null;
async function handleRequest(user) {
currentUser = user; // "hope" 저장
await fetchData(); // 다른 요청이 들어오면?
// 이 사이에 다른 요청: currentUser = "someone"
console.log(currentUser); // "someone"이 출력됨
}
// 동시에 두 요청
handleRequest("hope");
handleRequest("someone");
문제 2. React 에서 실제로 겪는 문제
function SubmitButton() {
const [isPending, startTransition] = useTransition();
const handleClick = () => {
console.log('클릭!', isPending); // false
startTransition(async () => {
console.log('시작', isPending); // true
await api.submit();
console.log('API 후', isPending); // ???
// 여기가 문제. 리액트는 이게 같은 transition인지 모름
navigate('/success');
console.log('navigate 후', isPending);
});
};
return <button disabled={isPending}>제출</button>;
}
- 즉, 리액트의 시점에서는 startTransition의 인자로 받는 callback 만 추적이 가능하고, callback 내부의 비동기 작업은 추적이 불가능함.
어떻게 AsyncContext 가 사용될까?
// 1. AsyncContext 생성
const userContext = new AsyncContext.Variable();
// 2. 값 설정하고 비동기 작업 실행
async function handleRequest(userId) {
await userContext.run(userId, async () => {
// 이 안의 모든 비동기 작업에서 userId 접근 가능
await step1();
await step2();
setTimeout(() => {
console.log(userContext.get()); // userId 출력!
}, 1000);
});
}
// 3. 어디서든 값 가져오기
async function step1() {
const userId = userContext.get(); // Hope
await saveLog(userId);
}
async function step2() {
const userId = userContext.get(); // Hope
await updateData(userId);
}
어떻게 동작하는가?
- 실행컨텍스트처럼, 모든 비동기 작업마다 고유의 id 를 부여하고, 현재 실행중인 비동기 작업의 id 반환..
- 즉, 비동기 작업이 생성될 때의 컨텍스트를 캡쳐하고, 실행될 때 그 컨텍스트를 복원하는 것.
실제로 비슷한 개념 (node.js 의 Async Local Storage https://nodejs.org/api/async_context.html#class-asynclocalstorage)