게시일: 2025년 3월 6일
긴 작업으로 인해 기본 스레드가 계속 사용 중이면 페이지가 느리고 응답하지 않는 것처럼 느껴집니다. 기본 스레드가 사용자 입력에 응답하는 것과 같은 다른 중요한 작업을 수행할 수 없기 때문입니다. 따라서 더 복잡한 맞춤 구성요소는 말할 것도 없고 내장된 양식 컨트롤조차 사용자에게 페이지가 멈춘 것처럼 깨진 것처럼 보일 수 있습니다.
scheduler.yield()
는 기본 스레드에 양보하여 브라우저가 대기 중인 우선순위가 높은 작업을 실행하도록 허용한 다음 중단된 지점에서 실행을 계속하는 방법입니다. 이렇게 하면 페이지의 응답성이 높아지고 결과적으로 Interaction to Next Paint (INP)가 개선됩니다.
scheduler.yield
는 명시된 대로 정확하게 작동하는 인체공학적 API를 제공합니다. 즉, 호출된 함수의 실행이 await scheduler.yield()
표현식에서 일시중지되고 기본 스레드로 양보되어 작업이 중단됩니다. 함수의 나머지 부분(함수의 연속이라고 함)의 실행은 새 이벤트 루프 작업에서 실행되도록 예약됩니다.
async function respondToUserClick() {
giveImmediateFeedback();
await scheduler.yield(); // Yield to the main thread.
slowerComputation();
}
scheduler.yield
의 구체적인 이점은 yield 후의 연속이 페이지에서 대기열에 추가된 다른 유사한 작업을 실행하기 전에 실행되도록 예약된다는 것입니다. 새 작업을 시작하는 것보다 기존 작업의 지속을 우선시합니다.
setTimeout
또는 scheduler.postTask
와 같은 함수를 사용하여 작업을 분할할 수도 있지만 이러한 연속은 일반적으로 이미 대기열에 추가된 새 작업 후에 실행되므로 기본 스레드에 양보한 후 작업을 완료하는 데 긴 지연이 발생할 수 있습니다.
양보 후 우선순위가 지정된 연속
scheduler.yield
는 우선순위가 지정된 작업 예약 API의 일부입니다. 웹 개발자는 일반적으로 이벤트 루프가 명시적 우선순위 측면에서 작업을 실행하는 순서에 관해 이야기하지 않지만 상대적 우선순위는 항상 존재합니다. 예를 들어 대기열에 추가된 setTimeout
콜백 후에 실행되는 requestIdleCallback
콜백이나 setTimeout(callback, 0)
로 대기열에 추가된 작업 전에 실행되는 트리거된 입력 이벤트 리스너가 있습니다.
우선순위가 지정된 작업 예약은 이를 더 명시적으로 만들어 어떤 작업이 다른 작업보다 먼저 실행되는지 쉽게 파악할 수 있으며, 필요한 경우 우선순위를 조정하여 실행 순서를 변경할 수 있습니다.
언급한 바와 같이 scheduler.yield()
로 양보한 후 함수의 계속 실행은 다른 작업을 시작하는 것보다 높은 우선순위를 갖습니다. 안내 개념은 다른 작업으로 이동하기 전에 작업의 연속이 먼저 실행되어야 한다는 것입니다. 작업이 브라우저가 사용자 입력에 응답하는 것과 같은 다른 중요한 작업을 할 수 있도록 주기적으로 양보하는 적절한 코드인 경우 다른 유사한 작업보다 우선순위가 지정되어 양보에 대한 처벌을 받아서는 안 됩니다.
다음은 setTimeout
를 사용하여 서로 다른 작업에서 실행되도록 대기열에 추가된 두 함수의 예입니다.
setTimeout(myJob);
setTimeout(someoneElsesJob);
이 경우 두 setTimeout
호출이 바로 옆에 있지만 실제 페이지에서는 자체적으로 실행되도록 작업을 설정하는 서드 파티 스크립트와 퍼스트 파티 스크립트와 같이 완전히 다른 위치에서 호출될 수 있습니다. 또는 프레임워크의 스케줄러 깊숙이에서 트리거되는 별도의 구성요소의 두 작업일 수도 있습니다.
DevTools에서 이러한 작업은 다음과 같이 표시될 수 있습니다.
myJob
는 긴 작업으로 플래그가 지정되어 실행되는 동안 브라우저가 다른 작업을 실행하지 못하도록 차단합니다. 퍼스트 파티 스크립트에서 가져온 것으로 가정하면 다음과 같이 나눌 수 있습니다.
function myJob() {
// Run part 1.
myJobPart1();
// Yield with setTimeout() to break up long task, then run part2.
setTimeout(myJobPart2, 0);
}
myJobPart2
가 myJob
내에서 setTimeout
와 함께 실행되도록 예약되었지만 이 예약은 someoneElsesJob
가 이미 예약된 후에 실행되므로 실행은 다음과 같이 표시됩니다.
setTimeout
를 사용하여 작업을 분할했으므로 브라우저가 myJob
중간에 응답할 수 있지만 이제 myJob
의 두 번째 부분은 someoneElsesJob
가 완료된 후에만 실행됩니다.
경우에 따라 괜찮을 수도 있지만 일반적으로는 최적의 방법이 아닙니다. myJob
는 페이지가 사용자 입력에 계속 응답할 수 있도록 기본 스레드에 양보하는 것이지 기본 스레드를 완전히 포기하는 것이 아닙니다. someoneElsesJob
가 특히 느리거나 someoneElsesJob
외에 다른 작업도 많이 예약된 경우 myJob
의 후반부가 실행되기까지 오랜 시간이 걸릴 수 있습니다. 개발자가 myJob
에 setTimeout
를 추가할 때 의도한 것은 아니었을 것입니다.
scheduler.yield()
를 입력합니다. 그러면 이를 호출하는 함수의 연속이 다른 유사한 작업을 시작하는 것보다 약간 높은 우선순위 큐에 배치됩니다. myJob
가 사용하도록 변경된 경우:
async function myJob() {
// Run part 1.
myJobPart1();
// Yield with scheduler.yield() to break up long task, then run part2.
await scheduler.yield();
myJobPart2();
}
이제 실행은 다음과 같습니다.
브라우저는 여전히 응답할 수 있지만 이제 myJob
작업의 연속이 새 작업 someoneElsesJob
시작보다 우선순위가 높으므로 someoneElsesJob
가 시작되기 전에 myJob
가 완료됩니다. 이는 기본 스레드를 완전히 포기하는 것이 아니라 응답성을 유지하기 위해 기본 스레드에 양보해야 한다는 기대치에 훨씬 더 가깝습니다.
우선순위 상속
더 큰 우선순위가 지정된 작업 일정 API의 일부인 scheduler.yield()
는 scheduler.postTask()
에서 사용할 수 있는 명시적 우선순위와 잘 호환됩니다. 명시적으로 설정된 우선순위가 없으면 scheduler.postTask()
콜백 내의 scheduler.yield()
는 기본적으로 이전 예와 동일하게 작동합니다.
하지만 낮은 'background'
우선순위를 사용하는 등 우선순위가 설정된 경우:
async function lowPriorityJob() {
part1();
await scheduler.yield();
part2();
}
scheduler.postTask(lowPriorityJob, {priority: 'background'});
연속은 다른 'background'
작업보다 우선순위가 높게 예약되므로 대기 중인 'background'
작업보다 우선순위가 지정된 연속이 먼저 실행되지만 다른 기본 또는 우선순위가 높은 작업보다는 우선순위가 낮습니다. 'background'
작업으로 유지됩니다.
즉, 'background'
scheduler.postTask()
(또는 requestIdleCallback
)로 우선순위가 낮은 작업을 예약하면 scheduler.yield()
내의 연속도 대부분의 다른 작업이 완료되고 기본 스레드가 유휴 상태가 될 때까지 기다린 후 실행됩니다. 이는 우선순위가 낮은 작업에서 원하는 동작입니다.
API 사용 방법
현재 scheduler.yield()
는 Chromium 기반 브라우저에서만 사용할 수 있으므로 이를 사용하려면 기능 감지를 수행하고 다른 브라우저의 경우 양보하는 보조 방식으로 대체해야 합니다.
scheduler-polyfill
는 scheduler.postTask
및 scheduler.yield
의 작은 폴리필로, 내부적으로 메서드 조합을 사용하여 다른 브라우저의 일정 API의 많은 기능을 에뮬레이션합니다 (scheduler.yield()
우선순위 상속은 지원되지 않음).
폴리필을 피하려는 경우 한 가지 방법은 setTimeout()
를 사용하여 양보하고 우선순위가 지정된 연속의 손실을 수용하거나, 허용되지 않는 경우 지원되지 않는 브라우저에서 양보하지 않는 것입니다. 자세한 내용은 긴 작업 최적화의 scheduler.yield()
문서를 참고하세요.
wicg-task-scheduling
유형을 사용하여 scheduler.yield()
를 기능 감지하고 직접 대체 항목을 추가하는 경우 유형 검사와 IDE 지원을 받을 수도 있습니다.
자세히 알아보기
API 및 API가 작업 우선순위 및 scheduler.postTask()
와 상호작용하는 방식에 관한 자세한 내용은 MDN의 scheduler.yield()
및 우선순위가 지정된 작업 예약 문서를 참고하세요.
긴 작업, 긴 작업이 사용자 환경에 미치는 영향, 긴 작업에 대한 조치에 대해 자세히 알아보려면 긴 작업 최적화를 참고하세요.