@@ -91,6 +91,13 @@ const localClearTimeout =
9191const localSetImmediate =
9292 typeof setImmediate !== 'undefined' ? setImmediate : null ; // IE and Node.js + jsdom
9393
94+ const isInputPending =
95+ typeof navigator !== 'undefined' &&
96+ navigator . scheduling !== undefined &&
97+ navigator . scheduling . isInputPending !== undefined
98+ ? navigator . scheduling . isInputPending . bind ( navigator . scheduling )
99+ : null ;
100+
94101function advanceTimers ( currentTime ) {
95102 // Check for tasks that are no longer delayed and add them to the queue.
96103 let timer = peek ( timerQueue ) ;
@@ -418,48 +425,58 @@ let taskTimeoutID = -1;
418425// thread, like user events. By default, it yields multiple times per frame.
419426// It does not attempt to align with frame boundaries, since most tasks don't
420427// need to be frame aligned; for those that do, use requestAnimationFrame.
421- let yieldInterval = 5 ;
428+ // TODO: Make these configurable
429+ let frameInterval = 5 ;
430+ const continuousInputInterval = 50 ;
431+ const maxInterval = 300 ;
422432let startTime = - 1 ;
423433
424- // TODO: Make this configurable
425- // TODO: Adjust this based on priority?
426- const maxYieldInterval = 300 ;
427434let needsPaint = false ;
428435
429436function shouldYieldToHost ( ) {
430437 const timeElapsed = getCurrentTime ( ) - startTime ;
431- if (
432- enableIsInputPending &&
433- navigator !== undefined &&
434- navigator . scheduling !== undefined &&
435- navigator . scheduling . isInputPending !== undefined
436- ) {
437- const scheduling = navigator . scheduling ;
438- if ( timeElapsed >= yieldInterval ) {
439- // There's no time left. We may want to yield control of the main
440- // thread, so the browser can perform high priority tasks. The main ones
441- // are painting and user input. If there's a pending paint or a pending
442- // input, then we should yield. But if there's neither, then we can
443- // yield less often while remaining responsive. We'll eventually yield
444- // regardless, since there could be a pending paint that wasn't
445- // accompanied by a call to `requestPaint`, or other main thread tasks
446- // like network events.
447- if ( needsPaint || scheduling . isInputPending ( ) ) {
448- // There is either a pending paint or a pending input.
449- return true ;
438+ if ( timeElapsed < frameInterval ) {
439+ // The main thread has only been blocked for a really short amount of time;
440+ // smaller than a single frame. Don't yield yet.
441+ return false ;
442+ }
443+
444+ // The main thread has been blocked for a non-negligible amount of time. We
445+ // may want to yield control of the main thread, so the browser can perform
446+ // high priority tasks. The main ones are painting and user input. If there's
447+ // a pending paint or a pending input, then we should yield. But if there's
448+ // neither, then we can yield less often while remaining responsive. We'll
449+ // eventually yield regardless, since there could be a pending paint that
450+ // wasn't accompanied by a call to `requestPaint`, or other main thread tasks
451+ // like network events.
452+ if ( enableIsInputPending ) {
453+ if ( needsPaint ) {
454+ // There's a pending paint (signaled by `requestPaint`). Yield now.
455+ return true ;
456+ }
457+ if ( timeElapsed < continuousInputInterval ) {
458+ // We haven't blocked the thread for that long. Only yield if there's a
459+ // pending discrete input (e.g. click). It's OK if there's pending
460+ // continuous input (e.g. mouseover).
461+ if ( isInputPending !== null ) {
462+ return isInputPending ( ) ;
463+ }
464+ } else if ( timeElapsed < maxInterval ) {
465+ // Yield if there's either a pending discrete or continuous input.
466+ if ( isInputPending !== null ) {
467+ return isInputPending ( { includeContinuous : true } ) ;
450468 }
451- // There's no pending input. Only yield if we've reached the max
452- // yield interval.
453- return timeElapsed >= maxYieldInterval ;
454469 } else {
455- // There's still time left in the frame.
456- return false ;
470+ // We've blocked the thread for a long time. Even if there's no pending
471+ // input, there may be some other scheduled work that we don't know about,
472+ // like a network event. Yield now.
473+ return true ;
457474 }
458- } else {
459- // `isInputPending` is not available. Since we have no way of knowing if
460- // there's pending input, always yield at the end of the frame.
461- return timeElapsed >= yieldInterval ;
462475 }
476+
477+ // Either there's no pending input, or `isInputPending` isn't available.
478+ // Yield now.
479+ return true ;
463480}
464481
465482function requestPaint ( ) {
@@ -485,10 +502,10 @@ function forceFrameRate(fps) {
485502 return ;
486503 }
487504 if ( fps > 0 ) {
488- yieldInterval = Math . floor ( 1000 / fps ) ;
505+ frameInterval = Math . floor ( 1000 / fps ) ;
489506 } else {
490507 // reset the framerate
491- yieldInterval = 5 ;
508+ frameInterval = 5 ;
492509 }
493510}
494511
0 commit comments