@@ -91,6 +91,13 @@ const localClearTimeout =
91
91
const localSetImmediate =
92
92
typeof setImmediate !== 'undefined' ? setImmediate : null ; // IE and Node.js + jsdom
93
93
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
+
94
101
function advanceTimers ( currentTime ) {
95
102
// Check for tasks that are no longer delayed and add them to the queue.
96
103
let timer = peek ( timerQueue ) ;
@@ -418,48 +425,57 @@ let taskTimeoutID = -1;
418
425
// thread, like user events. By default, it yields multiple times per frame.
419
426
// It does not attempt to align with frame boundaries, since most tasks don't
420
427
// 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 ;
422
432
let startTime = - 1 ;
423
433
424
- // TODO: Make this configurable
425
- // TODO: Adjust this based on priority?
426
- const maxYieldInterval = 300 ;
427
434
let needsPaint = false ;
428
435
429
436
function shouldYieldToHost ( ) {
430
437
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 } ) ;
450
468
}
451
- // There's no pending input. Only yield if we've reached the max
452
- // yield interval.
453
- return timeElapsed >= maxYieldInterval ;
454
469
} 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 ;
457
474
}
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 ;
462
475
}
476
+
477
+ // `isInputPending` isn't available. Yield now.
478
+ return true ;
463
479
}
464
480
465
481
function requestPaint ( ) {
@@ -485,10 +501,10 @@ function forceFrameRate(fps) {
485
501
return ;
486
502
}
487
503
if ( fps > 0 ) {
488
- yieldInterval = Math . floor ( 1000 / fps ) ;
504
+ frameInterval = Math . floor ( 1000 / fps ) ;
489
505
} else {
490
506
// reset the framerate
491
- yieldInterval = 5 ;
507
+ frameInterval = 5 ;
492
508
}
493
509
}
494
510
0 commit comments