Fix message loop behavior when host callback is cancelled (#16407)
* Add a regression test for cancelCallback with message loop * If there's nothing scheduled, we're not running * Add more tests from #16271
Dan Abramov committed
Aug 19, 2019 at 21:20 UTC
2d68bd09603fc54d927240003810db9aa6f2e3e5
2 files changed
+74
-1
packages/scheduler/src/__tests__/SchedulerBrowser-test.internal.js
+72
-1
@@ -15,6 +15,7 @@
15
let Scheduler;
16
let runtime;
17
let performance;
18
+let cancelCallback;
19
let scheduleCallback;
20
let NormalPriority;
21
@@ -52,6 +53,7 @@ describe('SchedulerBrowser', () => {
53
performance = window.performance;
54
require('scheduler/src/SchedulerFeatureFlags').enableMessageLoopImplementation = enableMessageLoopImplementation;
55
Scheduler = require('scheduler');
56
+ cancelCallback = Scheduler.unstable_cancelCallback;
57
scheduleCallback = Scheduler.unstable_scheduleCallback;
58
NormalPriority = Scheduler.unstable_NormalPriority;
59
});
@@ -360,6 +362,15 @@ describe('SchedulerBrowser', () => {
362
const enableMessageLoopImplementation = true;
363
beforeAndAfterHooks(enableMessageLoopImplementation);
364
365
+ it('task that finishes before deadline', () => {
366
+ scheduleCallback(NormalPriority, () => {
367
+ runtime.log('Task');
368
+ });
369
+ runtime.assertLog(['Post Message']);
370
+ runtime.fireMessageEvent();
371
+ runtime.assertLog(['Message Event', 'Task']);
372
+ });
373
+
374
it('task with continutation', () => {
375
scheduleCallback(NormalPriority, () => {
376
runtime.log('Task');
@@ -385,7 +396,48 @@ describe('SchedulerBrowser', () => {
396
runtime.assertLog(['Message Event', 'Continuation']);
397
});
398
388
- it('task that throws', () => {
399
+ it('multiple tasks', () => {
400
+ scheduleCallback(NormalPriority, () => {
401
+ runtime.log('A');
402
+ });
403
+ scheduleCallback(NormalPriority, () => {
404
+ runtime.log('B');
405
+ });
406
+ runtime.assertLog(['Post Message']);
407
+ runtime.fireMessageEvent();
408
+ runtime.assertLog(['Message Event', 'A', 'B']);
409
+ });
410
+
411
+ it('multiple tasks with a yield in between', () => {
412
+ scheduleCallback(NormalPriority, () => {
413
+ runtime.log('A');
414
+ runtime.advanceTime(4999);
415
+ });
416
+ scheduleCallback(NormalPriority, () => {
417
+ runtime.log('B');
418
+ });
419
+ runtime.assertLog(['Post Message']);
420
+ runtime.fireMessageEvent();
421
+ runtime.assertLog([
422
+ 'Message Event',
423
+ 'A',
424
+ // Ran out of time. Post a continuation event.
425
+ 'Post Message',
426
+ ]);
427
+ runtime.fireMessageEvent();
428
+ runtime.assertLog(['Message Event', 'B']);
429
+ });
430
+
431
+ it('cancels tasks', () => {
432
+ const task = scheduleCallback(NormalPriority, () => {
433
+ runtime.log('Task');
434
+ });
435
+ runtime.assertLog(['Post Message']);
436
+ cancelCallback(task);
437
+ runtime.assertLog([]);
438
+ });
439
+
440
+ it('throws when a task errors then continues in a new event', () => {
441
scheduleCallback(NormalPriority, () => {
442
runtime.log('Oops!');
443
throw Error('Oops!');
@@ -418,5 +470,24 @@ describe('SchedulerBrowser', () => {
470
runtime.fireMessageEvent();
471
runtime.assertLog(['Message Event', 'B']);
472
});
473
+
474
+ it('schedule new task after a cancellation', () => {
475
+ let handle = scheduleCallback(NormalPriority, () => {
476
+ runtime.log('A');
477
+ });
478
+
479
+ runtime.assertLog(['Post Message']);
480
+ cancelCallback(handle);
481
+
482
+ runtime.fireMessageEvent();
483
+ runtime.assertLog(['Message Event']);
484
+
485
+ scheduleCallback(NormalPriority, () => {
486
+ runtime.log('B');
487
+ });
488
+ runtime.assertLog(['Post Message']);
489
+ runtime.fireMessageEvent();
490
+ runtime.assertLog(['Message Event', 'B']);
491
+ });
492
});
493
});
packages/scheduler/src/forks/SchedulerHostConfig.default.js
+2
@@ -235,6 +235,8 @@ if (
235
port.postMessage(null);
236
throw error;
237
}
238
+ } else {
239
+ isMessageLoopRunning = false;
240
}
241
// Yielding to the browser will give it a chance to paint, so we can
242
// reset this.