@samitouri / QOS-React / commits / 7e348acab7

Use invokeGuardedCallback in place of Fiber try-catch blocks

Only in DEV. In production, continue using a local try-catch.

Andrew Clark committed Feb 22, 2017 at 14:29 UTC 7e348acab777a29ad86564c4d14dddc385cea8e1
2 files changed +68 -31
src/renderers/shared/fiber/ReactFiberCommitWork.js
+25 -11
@@ -26,6 +26,7 @@ var {
26 } = ReactTypeOfWork;
27 var { commitCallbacks } = require('ReactFiberUpdateQueue');
28 var { onCommitUnmount } = require('ReactFiberDevToolsHook');
29 +var { invokeGuardedCallback } = require('ReactErrorUtils');
30
31 var {
32 Placement,
@@ -54,22 +55,35 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
55
56 // Capture errors so they don't interrupt unmounting.
57 function safelyCallComponentWillUnmount(current, instance) {
57 - try {
58 - instance.componentWillUnmount();
59 - } catch (error) {
60 - captureError(current, error);
58 + if (__DEV__) {
59 + const unmountError = invokeGuardedCallback(null, instance.componentWillUnmount, instance);
60 + if (unmountError) {
61 + captureError(current, unmountError);
62 + }
63 + } else {
64 + try {
65 + instance.componentWillUnmount();
66 + } catch (unmountError) {
67 + captureError(current, unmountError);
68 + }
69 }
70 }
71
64 - // Capture errors so they don't interrupt unmounting.
72 function safelyDetachRef(current : Fiber) {
66 - try {
67 - const ref = current.ref;
68 - if (ref !== null) {
69 - ref(null);
73 + const ref = current.ref;
74 + if (ref !== null) {
75 + if (__DEV__) {
76 + const refError = invokeGuardedCallback(null, ref, null, null);
77 + if (refError !== null) {
78 + captureError(current, refError);
79 + }
80 + } else {
81 + try {
82 + ref(null);
83 + } catch (refError) {
84 + captureError(current, refError);
85 + }
86 }
71 - } catch (error) {
72 - captureError(current, error);
87 }
88 }
89
src/renderers/shared/fiber/ReactFiberScheduler.js
+43 -20
@@ -35,6 +35,7 @@ var {
35 getStackAddendumByWorkInProgressFiber,
36 } = require('ReactComponentTreeHook');
37 var { logCapturedError } = require('ReactFiberErrorLogger');
38 +var { invokeGuardedCallback } = require('ReactErrorUtils');
39
40 var ReactFiberBeginWork = require('ReactFiberBeginWork');
41 var ReactFiberCompleteWork = require('ReactFiberCompleteWork');
@@ -164,6 +165,9 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
165 // Keeps track of whether we're currently in a work loop.
166 let isPerformingWork : boolean = false;
167
168 + // Keeps track of whether the current deadline has expired.
169 + let deadlineHasExpired : boolean = false;
170 +
171 // Keeps track of whether we should should batch sync updates.
172 let isBatchingUpdates : boolean = false;
173
@@ -414,9 +418,17 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
418 // ref unmounts.
419 nextEffect = firstEffect;
420 while (nextEffect !== null) {
417 - try {
418 - commitAllHostEffects(finishedWork);
419 - } catch (error) {
421 + let error = null;
422 + if (__DEV__) {
423 + error = invokeGuardedCallback(null, commitAllHostEffects, null, finishedWork);
424 + } else {
425 + try {
426 + commitAllHostEffects(finishedWork);
427 + } catch (e) {
428 + error = e;
429 + }
430 + }
431 + if (error !== null) {
432 invariant(
433 nextEffect !== null,
434 'Should have next effect. This error is likely caused by a bug ' +
@@ -444,9 +456,17 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
456 // This pass also triggers any renderer-specific initial effects.
457 nextEffect = firstEffect;
458 while (nextEffect !== null) {
447 - try {
448 - commitAllLifeCycles(finishedWork, nextEffect);
449 - } catch (error) {
459 + let error = null;
460 + if (__DEV__) {
461 + error = invokeGuardedCallback(null, commitAllLifeCycles, null, finishedWork);
462 + } else {
463 + try {
464 + commitAllLifeCycles(finishedWork);
465 + } catch (e) {
466 + error = e;
467 + }
468 + }
469 + if (error !== null) {
470 invariant(
471 nextEffect !== null,
472 'Should have next effect. This error is likely caused by a bug ' +
@@ -675,7 +695,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
695 }
696 }
697
678 - function workLoop(priorityLevel, deadline : Deadline | null, deadlineHasExpired : boolean) : boolean {
698 + function workLoop(priorityLevel, deadline : Deadline | null) {
699 // Clear any errors.
700 clearErrors();
701
@@ -743,8 +763,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
763 if (hostRootTimeMarker) {
764 console.timeEnd(hostRootTimeMarker);
765 }
746 -
747 - return deadlineHasExpired;
766 }
767
768 function performWork(priorityLevel : PriorityLevel, deadline : Deadline | null) {
@@ -755,7 +773,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
773 );
774 isPerformingWork = true;
775 const isPerformingDeferredWork = Boolean(deadline);
758 - let deadlineHasExpired = false;
776
777 // This outer loop exists so that we can restart the work loop after
778 // catching an error. It also lets us flush Task work at the end of a
@@ -776,18 +793,25 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
793 // Nothing in performWork should be allowed to throw. All unsafe
794 // operations must happen within workLoop, which is extracted to a
795 // separate function so that it can be optimized by the JS engine.
779 - try {
780 - priorityContextBeforeReconciliation = priorityContext;
781 - priorityContext = nextPriorityLevel;
782 - deadlineHasExpired = workLoop(priorityLevel, deadline, deadlineHasExpired);
783 - } catch (error) {
796 + priorityContextBeforeReconciliation = priorityContext;
797 + let error = null;
798 + if (__DEV__) {
799 + error = invokeGuardedCallback(null, workLoop, null, priorityLevel, deadline);
800 + } else {
801 + try {
802 + workLoop(priorityLevel, deadline);
803 + } catch (e) {
804 + error = e;
805 + }
806 + }
807 + // Reset the priority context to its value before reconcilation.
808 + priorityContext = priorityContextBeforeReconciliation;
809 +
810 + if (error !== null) {
811 // We caught an error during either the begin or complete phases.
812 const failedWork = nextUnitOfWork;
813
814 if (failedWork !== null) {
788 - // Reset the priority context to its value before reconciliation.
789 - priorityContext = priorityContextBeforeReconciliation;
790 -
815 // "Capture" the error by finding the nearest boundary. If there is no
816 // error boundary, the nearest host container acts as one. If
817 // captureError returns null, the error was intentionally ignored.
@@ -818,8 +842,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
842 // inside resetAfterCommit.
843 fatalError = error;
844 }
821 - } finally {
822 - priorityContext = priorityContextBeforeReconciliation;
845 }
846
847 // Stop performing work
@@ -862,6 +884,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
884
885 // We're done performing work. Time to clean up.
886 isPerformingWork = false;
887 + deadlineHasExpired = false;
888 fatalError = null;
889 firstUncaughtError = null;
890 capturedErrors = null;