[{"data":1,"prerenderedAt":1127},["ShallowReactive",2],{"content:\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fscheduler-yield-vs-settimeout-0-for-yielding":3,"surroundings:\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fscheduler-yield-vs-settimeout-0-for-yielding":1118},{"id":4,"title":5,"body":6,"description":1097,"extension":1098,"meta":1099,"navigation":1111,"path":1112,"seo":1113,"stem":1116,"__hash__":1117},"content\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fscheduler-yield-vs-settimeout-0-for-yielding\u002Findex.md","scheduler.yield() vs setTimeout(0) for Yielding",{"type":7,"value":8,"toc":1088},"minimark",[9,14,47,50,55,99,103,197,353,357,366,372,382,393,397,402,588,596,703,706,715,775,785,789,796,977,984,988,1004,1013,1022,1031,1040,1049,1053,1073,1078,1081,1084],[10,11,13],"h1",{"id":12},"scheduleryield-vs-settimeout0-which-yield-should-you-use","scheduler.yield() vs setTimeout(0): Which Yield Should You Use?",[15,16,17,18,23,24,28,29,33,34,38,39,42,43,46],"p",{},"This comparison sits under ",[19,20,22],"a",{"href":21},"\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002F","Optimizing INP with scheduler.yield()"," inside ",[19,25,27],{"href":26},"\u002Fcore-web-vitals-measurement\u002F","Core Web Vitals & Measurement",". Both techniques break a long task into shorter ones so a queued interaction can be processed inside the ",[30,31,32],"code",{},"200ms"," budget. The difference is what happens to ",[35,36,37],"em",{},"your"," remaining work: ",[30,40,41],{},"setTimeout(0)"," sends the continuation to the back of the task queue, where anything scheduled in the meantime — including a third-party timer — runs first. ",[30,44,45],{},"scheduler.yield()"," returns a promise that resumes at a higher priority than newly queued tasks.",[15,48,49],{},"That difference decides whether a long job finishes in a predictable time or is starved by other work while the page appears busy.",[51,52,54],"h2",{"id":53},"rapid-diagnosis-are-your-yields-being-overtaken","Rapid Diagnosis: Are Your Yields Being Overtaken?",[56,57,58,69,83,93],"ul",{},[59,60,61,65,66,68],"li",{},[62,63,64],"strong",{},"Look for growing gaps between chunks."," Record the long job in the Performance panel; with ",[30,67,41],{}," you will often see unrelated tasks interleaved between your chunks.",[59,70,71,74,75,78,79,82],{},[62,72,73],{},"Time the total."," A job that takes ",[30,76,77],{},"400ms"," unsplit but ",[30,80,81],{},"1.8s"," when chunked with timeouts is being overtaken, not slowed by the yield itself.",[59,84,85,88,89,92],{},[62,86,87],{},"Check clamping."," Nested ",[30,90,91],{},"setTimeout"," calls are clamped to a minimum delay after several levels, adding milliseconds per chunk that compound across hundreds of iterations.",[59,94,95,98],{},[62,96,97],{},"Confirm the interaction actually improved."," Both yields fix input responsiveness; the question here is the cost to throughput, not to INP.",[51,100,102],{"id":101},"the-comparison","The Comparison",[104,105,106,124],"table",{},[107,108,109],"thead",{},[110,111,112,116,120],"tr",{},[113,114,115],"th",{},"Axis",[113,117,118],{},[30,119,45],{},[113,121,122],{},[30,123,41],{},[125,126,127,139,150,161,175,186],"tbody",{},[110,128,129,133,136],{},[130,131,132],"td",{},"Continuation priority",[130,134,135],{},"Ahead of newly queued tasks",[130,137,138],{},"Back of the task queue",[110,140,141,144,147],{},[130,142,143],{},"Starvation risk",[130,145,146],{},"Low",[130,148,149],{},"Real — other timers can overtake",[110,151,152,155,158],{},[130,153,154],{},"Nesting clamp",[130,156,157],{},"None",[130,159,160],{},"Clamped after ~5 nested levels",[110,162,163,166,172],{},[130,164,165],{},"Ergonomics",[130,167,168,171],{},[30,169,170],{},"await"," inside a loop",[130,173,174],{},"Promise wrapper or callback chaining",[110,176,177,180,183],{},[130,178,179],{},"Support",[130,181,182],{},"Chromium 129+; polyfill available",[130,184,185],{},"Universal",[110,187,188,191,194],{},[130,189,190],{},"INP effect",[130,192,193],{},"Equivalent — both create yield points",[130,195,196],{},"Equivalent",[198,199,206,207,214,215,214,219,214,223,214,232,214,239,214,243,214,252,214,259,214,263,214,267,214,270,214,274,214,278,214,282,214,285,214,289,214,292,214,295,214,298,214,302,214,308,214,311,214,314,214,317,214,319,214,322,214,325,214,328,214,331,214,334,214,337,214,340,214,342,214,348,214],"svg",{"xmlns":200,"viewBox":201,"width":202,"role":203,"ariaLabel":204,"style":205},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","0 0 760 252","100%","img","Two main-thread timelines: with setTimeout the four work chunks are interleaved with unrelated tasks and finish late, while with scheduler.yield the chunks resume immediately after each yield point and finish early","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)","\n  ",[208,209],"rect",{"className":210,"x":212,"y":212,"width":202,"height":202,"fill":213},[211],"svg-canvas","0","#ffffff"," ",[216,217,218],"title",{},"What happens to your continuation after each yield",[220,221,222],"desc",{},"With setTimeout zero the continuation goes to the back of the queue and unrelated tasks run between chunks; with scheduler.yield the continuation resumes ahead of newly queued work, so the job completes sooner.",[208,224],{"x":225,"y":225,"width":226,"height":227,"rx":228,"fill":229,"stroke":230,"style":231},"1","758","250","10","none","currentColor","stroke-opacity:0.18",[233,234,238],"text",{"x":235,"y":236,"fill":230,"style":237},"24","32","font-size:16px;font-weight:700","Same chunks, different place in the queue",[233,240,41],{"x":235,"y":241,"fill":230,"style":242},"80","font-size:11.5px;font-weight:700",[208,244],{"x":245,"y":246,"width":247,"height":248,"rx":249,"fill":250,"stroke":250,"style":251},"130","62","60","28","4","#0466c8","fill-opacity:0.2;stroke-opacity:0.55",[233,253,258],{"x":254,"y":255,"fill":230,"style":256,"textAnchor":257},"160","81","font-size:10px;","middle","chunk 1",[208,260],{"x":261,"y":246,"width":241,"height":248,"rx":249,"fill":230,"stroke":230,"style":262},"196","fill-opacity:0.1;stroke-opacity:0.35",[233,264,266],{"x":265,"y":255,"fill":230,"style":256,"textAnchor":257},"236","other task",[208,268],{"x":269,"y":246,"width":247,"height":248,"rx":249,"fill":250,"stroke":250,"style":251},"282",[233,271,273],{"x":272,"y":255,"fill":230,"style":256,"textAnchor":257},"312","chunk 2",[208,275],{"x":276,"y":246,"width":277,"height":248,"rx":249,"fill":230,"stroke":230,"style":262},"348","96",[233,279,281],{"x":280,"y":255,"fill":230,"style":256,"textAnchor":257},"396","vendor timer",[208,283],{"x":284,"y":246,"width":247,"height":248,"rx":249,"fill":250,"stroke":250,"style":251},"450",[233,286,288],{"x":287,"y":255,"fill":230,"style":256,"textAnchor":257},"480","chunk 3",[208,290],{"x":291,"y":246,"width":241,"height":248,"rx":249,"fill":230,"stroke":230,"style":262},"516",[233,293,266],{"x":294,"y":255,"fill":230,"style":256,"textAnchor":257},"556",[208,296],{"x":297,"y":246,"width":247,"height":248,"rx":249,"fill":250,"stroke":250,"style":251},"602",[233,299,301],{"x":300,"y":255,"fill":230,"style":256,"textAnchor":257},"632","chunk 4",[233,303,307],{"x":304,"y":255,"fill":305,"style":306},"676","#b8860b","font-size:11px;font-weight:700","done",[233,309,45],{"x":235,"y":310,"fill":230,"style":242},"150",[208,312],{"x":245,"y":313,"width":247,"height":248,"rx":249,"fill":250,"stroke":250,"style":251},"132",[233,315,258],{"x":254,"y":316,"fill":230,"style":256,"textAnchor":257},"151",[208,318],{"x":261,"y":313,"width":247,"height":248,"rx":249,"fill":250,"stroke":250,"style":251},[233,320,273],{"x":321,"y":316,"fill":230,"style":256,"textAnchor":257},"226",[208,323],{"x":324,"y":313,"width":247,"height":248,"rx":249,"fill":250,"stroke":250,"style":251},"262",[233,326,288],{"x":327,"y":316,"fill":230,"style":256,"textAnchor":257},"292",[208,329],{"x":330,"y":313,"width":247,"height":248,"rx":249,"fill":250,"stroke":250,"style":251},"328",[233,332,301],{"x":333,"y":316,"fill":230,"style":256,"textAnchor":257},"358",[233,335,307],{"x":336,"y":316,"fill":305,"style":306},"402",[208,338],{"x":339,"y":313,"width":241,"height":248,"rx":249,"fill":230,"stroke":230,"style":262},"440",[233,341,266],{"x":287,"y":316,"fill":230,"style":256,"textAnchor":257},[343,344],"line",{"x1":235,"y1":345,"x2":346,"y2":345,"stroke":230,"style":347},"184","736","stroke-opacity:0.2",[233,349,352],{"x":235,"y":350,"fill":230,"style":351},"212","font-size:11.5px;fill-opacity:0.85","Both yield to input equally well. Only one guarantees your own work is not starved while it waits.",[51,354,356],{"id":355},"root-cause-analysis-why-the-timeout-yield-disappoints","Root Cause Analysis: Why the Timeout Yield Disappoints",[15,358,359,362,363,365],{},[62,360,361],{},"1. The task queue is shared."," A ",[30,364,41],{}," continuation is an ordinary task, so every timer, message callback and third-party job queued in the interim runs before it. On a page with an active analytics container this is not hypothetical.",[15,367,368,371],{},[62,369,370],{},"2. Nested timeouts are clamped."," After roughly five levels of nesting, browsers enforce a minimum delay of about 4ms. A loop that yields a thousand times therefore spends seconds doing nothing at all.",[15,373,374,377,378,381],{},[62,375,376],{},"3. Yielding too often is its own bug."," Both APIs cost a task boundary. Yielding every iteration of a tight loop can multiply total runtime; yield on a time budget instead — typically every ",[30,379,380],{},"40–50ms"," of work.",[15,383,384,387,388,392],{},[62,385,386],{},"4. Yielding does not reduce work."," Neither API makes the job cheaper. If the total is genuinely expensive, the answer is to move it off the main thread — see ",[19,389,391],{"href":390},"\u002Fcore-web-vitals-measurement\u002Foffloading-work-to-web-workers-with-comlink\u002F","offloading work to Web Workers with Comlink",".",[51,394,396],{"id":395},"step-by-step-resolution","Step-by-Step Resolution",[15,398,399],{},[62,400,401],{},"1. Yield on a time budget, not per item.",[403,404,409],"pre",{"className":405,"code":406,"language":407,"meta":408,"style":408},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","async function processAll(items, work) {\n  let last = performance.now();\n  for (const item of items) {\n    work(item);\n    if (performance.now() - last > 45) {       \u002F\u002F stay under the 50ms long-task line\n      await yieldToMain();\n      last = performance.now();\n    }\n  }\n}\n\u002F\u002F trade-off: a 45ms budget means an interaction can still wait up to 45ms for\n\u002F\u002F the current chunk. Lower it to ~25ms for latency-critical screens, accepting\n\u002F\u002F more task boundaries and slightly longer total runtime.\n","javascript","",[30,410,411,443,464,486,495,527,538,552,558,564,570,576,582],{"__ignoreMap":408},[412,413,415,419,422,426,430,434,437,440],"span",{"class":343,"line":414},1,[412,416,418],{"class":417},"sPARh","async",[412,420,421],{"class":417}," function",[412,423,425],{"class":424},"smZ65"," processAll",[412,427,429],{"class":428},"saISM","(",[412,431,433],{"class":432},"sQw3B","items",[412,435,436],{"class":428},", ",[412,438,439],{"class":432},"work",[412,441,442],{"class":428},") {\n",[412,444,446,449,452,455,458,461],{"class":343,"line":445},2,[412,447,448],{"class":417},"  let",[412,450,451],{"class":428}," last ",[412,453,454],{"class":417},"=",[412,456,457],{"class":428}," performance.",[412,459,460],{"class":424},"now",[412,462,463],{"class":428},"();\n",[412,465,467,470,473,476,480,483],{"class":343,"line":466},3,[412,468,469],{"class":417},"  for",[412,471,472],{"class":428}," (",[412,474,475],{"class":417},"const",[412,477,479],{"class":478},"sPXB4"," item",[412,481,482],{"class":417}," of",[412,484,485],{"class":428}," items) {\n",[412,487,489,492],{"class":343,"line":488},4,[412,490,491],{"class":424},"    work",[412,493,494],{"class":428},"(item);\n",[412,496,498,501,504,506,509,512,514,517,520,523],{"class":343,"line":497},5,[412,499,500],{"class":417},"    if",[412,502,503],{"class":428}," (performance.",[412,505,460],{"class":424},[412,507,508],{"class":428},"() ",[412,510,511],{"class":417},"-",[412,513,451],{"class":428},[412,515,516],{"class":417},">",[412,518,519],{"class":478}," 45",[412,521,522],{"class":428},") {       ",[412,524,526],{"class":525},"sjfSM","\u002F\u002F stay under the 50ms long-task line\n",[412,528,530,533,536],{"class":343,"line":529},6,[412,531,532],{"class":417},"      await",[412,534,535],{"class":424}," yieldToMain",[412,537,463],{"class":428},[412,539,541,544,546,548,550],{"class":343,"line":540},7,[412,542,543],{"class":428},"      last ",[412,545,454],{"class":417},[412,547,457],{"class":428},[412,549,460],{"class":424},[412,551,463],{"class":428},[412,553,555],{"class":343,"line":554},8,[412,556,557],{"class":428},"    }\n",[412,559,561],{"class":343,"line":560},9,[412,562,563],{"class":428},"  }\n",[412,565,567],{"class":343,"line":566},10,[412,568,569],{"class":428},"}\n",[412,571,573],{"class":343,"line":572},11,[412,574,575],{"class":525},"\u002F\u002F trade-off: a 45ms budget means an interaction can still wait up to 45ms for\n",[412,577,579],{"class":343,"line":578},12,[412,580,581],{"class":525},"\u002F\u002F the current chunk. Lower it to ~25ms for latency-critical screens, accepting\n",[412,583,585],{"class":343,"line":584},13,[412,586,587],{"class":525},"\u002F\u002F more task boundaries and slightly longer total runtime.\n",[15,589,590],{},[62,591,592,593,595],{},"2. Prefer ",[30,594,45],{}," with a graceful fallback.",[403,597,599],{"className":405,"code":598,"language":407,"meta":408,"style":408},"function yieldToMain() {\n  if ('scheduler' in globalThis && 'yield' in scheduler) return scheduler.yield();\n  return new Promise((resolve) => setTimeout(resolve, 0));\n}\n\u002F\u002F trade-off: the fallback is genuinely worse, not merely older — continuations\n\u002F\u002F can be overtaken. Where throughput matters and the API is missing, consider\n\u002F\u002F doing the work in a worker instead of chunking it on the main thread.\n",[30,600,601,611,650,684,688,693,698],{"__ignoreMap":408},[412,602,603,606,608],{"class":343,"line":414},[412,604,605],{"class":417},"function",[412,607,535],{"class":424},[412,609,610],{"class":428},"() {\n",[412,612,613,616,618,622,625,628,631,634,636,639,642,645,648],{"class":343,"line":445},[412,614,615],{"class":417},"  if",[412,617,472],{"class":428},[412,619,621],{"class":620},"sZ8jY","'scheduler'",[412,623,624],{"class":417}," in",[412,626,627],{"class":428}," globalThis ",[412,629,630],{"class":417},"&&",[412,632,633],{"class":620}," 'yield'",[412,635,624],{"class":417},[412,637,638],{"class":428}," scheduler) ",[412,640,641],{"class":417},"return",[412,643,644],{"class":428}," scheduler.",[412,646,647],{"class":424},"yield",[412,649,463],{"class":428},[412,651,652,655,658,661,664,667,670,673,676,679,681],{"class":343,"line":466},[412,653,654],{"class":417},"  return",[412,656,657],{"class":417}," new",[412,659,660],{"class":478}," Promise",[412,662,663],{"class":428},"((",[412,665,666],{"class":432},"resolve",[412,668,669],{"class":428},") ",[412,671,672],{"class":417},"=>",[412,674,675],{"class":424}," setTimeout",[412,677,678],{"class":428},"(resolve, ",[412,680,212],{"class":478},[412,682,683],{"class":428},"));\n",[412,685,686],{"class":343,"line":488},[412,687,569],{"class":428},[412,689,690],{"class":343,"line":497},[412,691,692],{"class":525},"\u002F\u002F trade-off: the fallback is genuinely worse, not merely older — continuations\n",[412,694,695],{"class":343,"line":529},[412,696,697],{"class":525},"\u002F\u002F can be overtaken. Where throughput matters and the API is missing, consider\n",[412,699,700],{"class":343,"line":540},[412,701,702],{"class":525},"\u002F\u002F doing the work in a worker instead of chunking it on the main thread.\n",[15,704,705],{},"Expected outcome: on supporting browsers the job finishes close to its unsplit duration while remaining interruptible; elsewhere it still yields correctly, just with less predictable throughput.",[15,707,708,214,711,714],{},[62,709,710],{},"3. Use explicit priorities for work that is genuinely lower value.",[30,712,713],{},"scheduler.postTask()"," lets you say so, rather than implying it with a timeout.",[403,716,718],{"className":405,"code":717,"language":407,"meta":408,"style":408},"scheduler.postTask(() => refreshRecommendations(), { priority: 'background' });\nscheduler.postTask(() => applyFilters(), { priority: 'user-blocking' });\n\u002F\u002F trade-off: 'background' work can be delayed indefinitely on a busy page.\n\u002F\u002F Anything with a deadline needs 'user-visible' plus your own timeout guard.\n",[30,719,720,745,765,770],{"__ignoreMap":408},[412,721,722,725,728,731,733,736,739,742],{"class":343,"line":414},[412,723,724],{"class":428},"scheduler.",[412,726,727],{"class":424},"postTask",[412,729,730],{"class":428},"(() ",[412,732,672],{"class":417},[412,734,735],{"class":424}," refreshRecommendations",[412,737,738],{"class":428},"(), { priority: ",[412,740,741],{"class":620},"'background'",[412,743,744],{"class":428}," });\n",[412,746,747,749,751,753,755,758,760,763],{"class":343,"line":445},[412,748,724],{"class":428},[412,750,727],{"class":424},[412,752,730],{"class":428},[412,754,672],{"class":417},[412,756,757],{"class":424}," applyFilters",[412,759,738],{"class":428},[412,761,762],{"class":620},"'user-blocking'",[412,764,744],{"class":428},[412,766,767],{"class":343,"line":466},[412,768,769],{"class":525},"\u002F\u002F trade-off: 'background' work can be delayed indefinitely on a busy page.\n",[412,771,772],{"class":343,"line":488},[412,773,774],{"class":525},"\u002F\u002F Anything with a deadline needs 'user-visible' plus your own timeout guard.\n",[15,776,777,780,781,392],{},[62,778,779],{},"4. Measure the interaction, not the loop."," Confirm the split actually helped INP by reproducing the interaction with the Performance panel's interaction track, as described in ",[19,782,784],{"href":783},"\u002Fcore-web-vitals-measurement\u002Fprofiling-event-handlers-for-inp\u002F","profiling event handlers for INP",[51,786,788],{"id":787},"verification","Verification",[15,790,791,792,795],{},"Record the same interaction before and after. Three things should be true afterwards: no single task exceeds ",[30,793,794],{},"50ms",", the interaction's processing duration is a fraction of the total job, and the job's overall wall-clock time has not ballooned. If the third has regressed sharply, you are either yielding too often or being starved by the timeout fallback.",[403,797,799],{"className":405,"code":798,"language":407,"meta":408,"style":408},"\u002F\u002F Compare total runtime and worst task across both yield strategies.\nperformance.mark('job:start');\nawait processAll(items, work);\nperformance.mark('job:end');\nconst total = performance.measure('job', 'job:start', 'job:end').duration;\nconst worst = Math.max(...performance.getEntriesByType('longtask').map((t) => t.duration), 0);\nconsole.log(`total ${Math.round(total)}ms, worst task ${Math.round(worst)}ms`);\n",[30,800,801,806,822,831,844,875,927],{"__ignoreMap":408},[412,802,803],{"class":343,"line":414},[412,804,805],{"class":525},"\u002F\u002F Compare total runtime and worst task across both yield strategies.\n",[412,807,808,811,814,816,819],{"class":343,"line":445},[412,809,810],{"class":428},"performance.",[412,812,813],{"class":424},"mark",[412,815,429],{"class":428},[412,817,818],{"class":620},"'job:start'",[412,820,821],{"class":428},");\n",[412,823,824,826,828],{"class":343,"line":466},[412,825,170],{"class":417},[412,827,425],{"class":424},[412,829,830],{"class":428},"(items, work);\n",[412,832,833,835,837,839,842],{"class":343,"line":488},[412,834,810],{"class":428},[412,836,813],{"class":424},[412,838,429],{"class":428},[412,840,841],{"class":620},"'job:end'",[412,843,821],{"class":428},[412,845,846,848,851,854,856,859,861,864,866,868,870,872],{"class":343,"line":497},[412,847,475],{"class":417},[412,849,850],{"class":478}," total",[412,852,853],{"class":417}," =",[412,855,457],{"class":428},[412,857,858],{"class":424},"measure",[412,860,429],{"class":428},[412,862,863],{"class":620},"'job'",[412,865,436],{"class":428},[412,867,818],{"class":620},[412,869,436],{"class":428},[412,871,841],{"class":620},[412,873,874],{"class":428},").duration;\n",[412,876,877,879,882,884,887,890,892,895,897,900,902,905,908,911,913,916,918,920,923,925],{"class":343,"line":529},[412,878,475],{"class":417},[412,880,881],{"class":478}," worst",[412,883,853],{"class":417},[412,885,886],{"class":428}," Math.",[412,888,889],{"class":424},"max",[412,891,429],{"class":428},[412,893,894],{"class":417},"...",[412,896,810],{"class":428},[412,898,899],{"class":424},"getEntriesByType",[412,901,429],{"class":428},[412,903,904],{"class":620},"'longtask'",[412,906,907],{"class":428},").",[412,909,910],{"class":424},"map",[412,912,663],{"class":428},[412,914,915],{"class":432},"t",[412,917,669],{"class":428},[412,919,672],{"class":417},[412,921,922],{"class":428}," t.duration), ",[412,924,212],{"class":478},[412,926,821],{"class":428},[412,928,929,932,935,937,940,943,945,948,950,953,956,959,961,963,965,967,970,972,975],{"class":343,"line":540},[412,930,931],{"class":428},"console.",[412,933,934],{"class":424},"log",[412,936,429],{"class":428},[412,938,939],{"class":620},"`total ${",[412,941,942],{"class":428},"Math",[412,944,392],{"class":620},[412,946,947],{"class":424},"round",[412,949,429],{"class":620},[412,951,952],{"class":428},"total",[412,954,955],{"class":620},")",[412,957,958],{"class":620},"}ms, worst task ${",[412,960,942],{"class":428},[412,962,392],{"class":620},[412,964,947],{"class":424},[412,966,429],{"class":620},[412,968,969],{"class":428},"worst",[412,971,955],{"class":620},[412,973,974],{"class":620},"}ms`",[412,976,821],{"class":428},[15,978,979,980,983],{},"In the field, the change should show up as reduced INP ",[35,981,982],{},"processing duration"," for the affected interaction target, with input delay unchanged. If input delay is what is high, yielding inside your handler was never the right fix — something else owns the thread.",[51,985,987],{"id":986},"faq","FAQ",[989,990,993,997],"details",{"className":991},[992],"faq-item",[994,995,996],"summary",{},"Is requestIdleCallback a third option?",[15,998,999,1000,1003],{},"For different work, yes. ",[30,1001,1002],{},"requestIdleCallback"," runs only when the browser is genuinely idle, which is right for non-urgent background jobs and wrong for anything the user is waiting on — on a busy page it may not run for seconds. Use it for prefetching and cleanup; use a yield loop for work that must complete promptly while staying interruptible.",[989,1005,1007,1010],{"className":1006},[992],[994,1008,1009],{},"Should I polyfill scheduler.yield()?",[15,1011,1012],{},"Only if you understand what the polyfill can and cannot restore. A polyfill reproduces the API shape on top of timers, so your code stays clean, but it cannot reproduce the browser's actual continuation priority — the starvation risk remains. That is fine; just do not assume the throughput characteristics carry over from the native implementation.",[989,1014,1016,1019],{"className":1015},[992],[994,1017,1018],{},"Does yielding hurt total throughput?",[15,1020,1021],{},"Slightly, and predictably: each yield costs a task boundary of a fraction of a millisecond, so a job yielding every 45ms adds a small percentage. The pathological case is yielding per iteration in a tight loop, where the boundaries dominate and runtime can multiply. Budget-based yielding keeps the overhead near noise while still keeping every task under the long-task line.",[989,1023,1025,1028],{"className":1024},[992],[994,1026,1027],{},"How do I choose the yield interval?",[15,1029,1030],{},"Work backwards from the latency you are willing to add to an interaction. The current chunk must finish before a queued event can be processed, so a 45ms budget can add up to 45ms of input delay in the worst case. On a screen where interactions are frequent and latency-critical — a type-ahead search, a drag handle — drop to 20–25ms and accept the extra task boundaries. On a background import or a bulk transform, 50ms keeps you just under the long-task line with minimal overhead.",[989,1032,1034,1037],{"className":1033},[992],[994,1035,1036],{},"Does yielding help if the work is in a framework render, not my loop?",[15,1038,1039],{},"Only indirectly. A framework's synchronous render is one task from the browser's point of view, and you cannot yield inside it from outside. What you can do is reduce how much it renders at once — smaller update batches, virtualised lists, or a concurrent renderer that splits the work itself. If your profile shows the long task inside the framework's commit phase rather than in your handler, a yield loop in your code will not move the metric.",[989,1041,1043,1046],{"className":1042},[992],[994,1044,1045],{},"Can I yield inside a Web Worker?",[15,1047,1048],{},"You can, and it is usually pointless. A worker has its own thread, so a long task there does not block input on the main thread — which is the entire problem yielding solves. The exception is a worker that must stay responsive to its own message queue, where the same budget-based pattern applies for the same reason. If work is expensive enough to need yielding on the main thread, moving it to a worker is generally the stronger fix.",[51,1050,1052],{"id":1051},"related","Related",[56,1054,1055,1060,1067],{},[59,1056,1057,1059],{},[19,1058,22],{"href":21}," — the full cooperative-scheduling workflow.",[59,1061,1062,1066],{},[19,1063,1065],{"href":1064},"\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fbreaking-up-long-tasks-in-react-event-handlers\u002F","Breaking up long tasks in React event handlers"," — applying the yield loop inside a framework's update cycle.",[59,1068,1069,1072],{},[19,1070,1071],{"href":390},"Offloading work to Web Workers with Comlink"," — when the right answer is to stop running the work on this thread at all.",[1074,1075,1077],"script",{"type":1076},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Choose a main-thread yielding strategy\",\n  \"description\": \"Compare scheduler.yield with setTimeout zero for splitting long tasks, yield on a time budget, add explicit task priorities, and verify against the interaction rather than the loop.\",\n  \"step\": [\n    { \"@type\": \"HowToStep\", \"position\": 1, \"name\": \"Yield on a time budget\", \"text\": \"Yield after roughly 45 milliseconds of work rather than on every iteration.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fscheduler-yield-vs-settimeout-0-for-yielding\u002F#step-by-step-resolution\" },\n    { \"@type\": \"HowToStep\", \"position\": 2, \"name\": \"Prefer the priority-preserving API\", \"text\": \"Use scheduler.yield where available and fall back to setTimeout zero elsewhere.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fscheduler-yield-vs-settimeout-0-for-yielding\u002F#step-by-step-resolution\" },\n    { \"@type\": \"HowToStep\", \"position\": 3, \"name\": \"Declare priorities explicitly\", \"text\": \"Schedule genuinely low-value work with postTask at background priority instead of implying it with a timeout.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fscheduler-yield-vs-settimeout-0-for-yielding\u002F#step-by-step-resolution\" },\n    { \"@type\": \"HowToStep\", \"position\": 4, \"name\": \"Verify against the interaction\", \"text\": \"Confirm no task exceeds 50ms and that total runtime has not ballooned from over-yielding.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fscheduler-yield-vs-settimeout-0-for-yielding\u002F#verification\" }\n  ]\n}\n",[1074,1079,1080],{"type":1076},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"scheduler.yield() vs setTimeout(0): Which Yield Should You Use?\",\n  \"description\": \"Both create yield points for input, but only one resumes ahead of newly queued tasks. A comparison of continuation priority, clamping and starvation risk.\",\n  \"datePublished\": \"2026-08-01\",\n  \"dateModified\": \"2026-08-01\",\n  \"author\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"publisher\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"mainEntityOfPage\": { \"@type\": \"WebPage\", \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fscheduler-yield-vs-settimeout-0-for-yielding\u002F\" }\n}\n",[1074,1082,1083],{"type":1076},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    { \"@type\": \"ListItem\", \"position\": 1, \"name\": \"Home\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 2, \"name\": \"Core Web Vitals & Measurement\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 3, \"name\": \"Optimizing INP with scheduler.yield()\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 4, \"name\": \"scheduler.yield() vs setTimeout(0) for Yielding\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fscheduler-yield-vs-settimeout-0-for-yielding\u002F\" }\n  ]\n}\n",[1085,1086,1087],"style",{},"html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}",{"title":408,"searchDepth":445,"depth":445,"links":1089},[1090,1091,1092,1093,1094,1095,1096],{"id":53,"depth":445,"text":54},{"id":101,"depth":445,"text":102},{"id":355,"depth":445,"text":356},{"id":395,"depth":445,"text":396},{"id":787,"depth":445,"text":788},{"id":986,"depth":445,"text":987},{"id":1051,"depth":445,"text":1052},"Why the classic setTimeout(0) yield can lose your place in the queue, and when the priority-preserving alternative is worth the feature detection.","md",{"slug":1100,"type":1101,"breadcrumb":1102,"datePublished":1110,"dateModified":1110},"scheduler-yield-vs-settimeout-0-for-yielding","article",[1103,1106,1107,1108],{"name":1104,"url":1105},"Home","\u002F",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":1109},"\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fscheduler-yield-vs-settimeout-0-for-yielding\u002F","2026-08-01",true,"\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fscheduler-yield-vs-settimeout-0-for-yielding",{"title":1114,"description":1115},"scheduler.yield() vs setTimeout(0) for Yielding to the Main Thread","Both hand control back to the browser, but only one resumes ahead of the task queue. Compare continuation priority, starvation risk, support and INP impact.","core-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fscheduler-yield-vs-settimeout-0-for-yielding\u002Findex","oyEZWc2ZVBnJ6VT0kCQosvsxvrvqig5arPOPaQBI7lA",[1119,1123],{"title":1120,"path":1121,"stem":1122,"children":-1},"Breaking Up Long Tasks in React Event Handlers","\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fbreaking-up-long-tasks-in-react-event-handlers","core-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fbreaking-up-long-tasks-in-react-event-handlers\u002Findex",{"title":1124,"path":1125,"stem":1126,"children":-1},"Profiling Event Handlers for INP","\u002Fcore-web-vitals-measurement\u002Fprofiling-event-handlers-for-inp","core-web-vitals-measurement\u002Fprofiling-event-handlers-for-inp\u002Findex",1785622407185]