Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion packages/scheduler/src/Scheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ var isPerformingWork = false;
var isHostCallbackScheduled = false;
var isHostTimeoutScheduled = false;

function scheduler_flushTaskAtPriority_Immediate(callback, didTimeout) {
return callback(didTimeout);
}
function scheduler_flushTaskAtPriority_UserBlocking(callback, didTimeout) {
return callback(didTimeout);
}
function scheduler_flushTaskAtPriority_Normal(callback, didTimeout) {
return callback(didTimeout);
}
function scheduler_flushTaskAtPriority_Low(callback, didTimeout) {
return callback(didTimeout);
}
function scheduler_flushTaskAtPriority_Idle(callback, didTimeout) {
return callback(didTimeout);
}

function flushTask(task, currentTime) {
// Remove the task from the list before calling the callback. That way the
// list is in a consistent state even if the callback throws.
Expand Down Expand Up @@ -83,7 +99,40 @@ function flushTask(task, currentTime) {
var continuationCallback;
try {
var didUserCallbackTimeout = task.expirationTime <= currentTime;
continuationCallback = callback(didUserCallbackTimeout);
// Add an extra function to the callstack. Profiling tools can use this
// to infer the priority of work that appears higher in the stack.
switch (currentPriorityLevel) {
case ImmediatePriority:
continuationCallback = scheduler_flushTaskAtPriority_Immediate(
callback,
didUserCallbackTimeout,
);
break;
case UserBlockingPriority:
continuationCallback = scheduler_flushTaskAtPriority_UserBlocking(
callback,
didUserCallbackTimeout,
);
break;
case NormalPriority:
continuationCallback = scheduler_flushTaskAtPriority_Normal(
callback,
didUserCallbackTimeout,
);
break;
case LowPriority:
continuationCallback = scheduler_flushTaskAtPriority_Low(
callback,
didUserCallbackTimeout,
);
break;
case IdlePriority:
continuationCallback = scheduler_flushTaskAtPriority_Idle(
callback,
didUserCallbackTimeout,
);
break;
}
} catch (error) {
throw error;
} finally {
Expand Down
71 changes: 71 additions & 0 deletions packages/scheduler/src/__tests__/Scheduler-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ let runWithPriority;
let ImmediatePriority;
let UserBlockingPriority;
let NormalPriority;
let LowPriority;
let IdlePriority;
let scheduleCallback;
let cancelCallback;
let wrapCallback;
Expand All @@ -31,6 +33,8 @@ describe('Scheduler', () => {
ImmediatePriority = Scheduler.unstable_ImmediatePriority;
UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
NormalPriority = Scheduler.unstable_NormalPriority;
LowPriority = Scheduler.unstable_LowPriority;
IdlePriority = Scheduler.unstable_IdlePriority;
scheduleCallback = Scheduler.unstable_scheduleCallback;
cancelCallback = Scheduler.unstable_cancelCallback;
wrapCallback = Scheduler.unstable_wrapCallback;
Expand Down Expand Up @@ -414,6 +418,73 @@ describe('Scheduler', () => {
]);
});

if (__DEV__) {
// Function names are minified in prod, though you could still infer the
// priority if you have sourcemaps.
it('adds extra function to the JS stack whose name includes the priority level', () => {
function inferPriorityFromCallstack() {
try {
throw Error();
} catch (e) {
const stack = e.stack;
const lines = stack.split('\n');
for (let i = lines.length - 1; i >= 0; i--) {
const line = lines[i];
const found = line.match(
/scheduler_flushTaskAtPriority_([A-Za-z]+)/,
);
if (found !== null) {
const priorityStr = found[1];
switch (priorityStr) {
case 'Immediate':
return ImmediatePriority;
case 'UserBlocking':
return UserBlockingPriority;
case 'Normal':
return NormalPriority;
case 'Low':
return LowPriority;
case 'Idle':
return IdlePriority;
}
}
}
return null;
}
}

scheduleCallback(ImmediatePriority, () =>
Scheduler.unstable_yieldValue(
'Immediate: ' + inferPriorityFromCallstack(),
),
);
scheduleCallback(UserBlockingPriority, () =>
Scheduler.unstable_yieldValue(
'UserBlocking: ' + inferPriorityFromCallstack(),
),
);
scheduleCallback(NormalPriority, () =>
Scheduler.unstable_yieldValue(
'Normal: ' + inferPriorityFromCallstack(),
),
);
scheduleCallback(LowPriority, () =>
Scheduler.unstable_yieldValue('Low: ' + inferPriorityFromCallstack()),
);
scheduleCallback(IdlePriority, () =>
Scheduler.unstable_yieldValue('Idle: ' + inferPriorityFromCallstack()),
);

expect(Scheduler).toFlushAndYield([
'Immediate: ' + ImmediatePriority,
'UserBlocking: ' + UserBlockingPriority,
'Normal: ' + NormalPriority,
'Low: ' + LowPriority,
'Idle: ' + IdlePriority,
]);
});
}

describe('delayed tasks', () => {
it('schedules a delayed task', () => {
scheduleCallback(
Expand Down