File size: 3,342 Bytes
b91e262 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | import { InvariantError } from '../../shared/lib/invariant-error'
import { createAtomicTimerGroup } from './app-render-scheduling'
import {
DANGEROUSLY_runPendingImmediatesAfterCurrentTask,
expectNoPendingImmediates,
} from '../node-environment-extensions/fast-set-immediate.external'
/**
* This is a utility function to make scheduling sequential tasks that run back to back easier.
* We schedule on the same queue (setTimeout) at the same time to ensure no other events can sneak in between.
*/
export function scheduleInSequentialTasks<R>(
render: () => R | Promise<R>,
followup: () => void
): Promise<R> {
if (process.env.NEXT_RUNTIME === 'edge') {
throw new InvariantError(
'`scheduleInSequentialTasks` should not be called in edge runtime.'
)
} else {
return new Promise((resolve, reject) => {
const scheduleTimeout = createAtomicTimerGroup()
let pendingResult: R | Promise<R>
scheduleTimeout(() => {
try {
DANGEROUSLY_runPendingImmediatesAfterCurrentTask()
pendingResult = render()
} catch (err) {
reject(err)
}
})
scheduleTimeout(() => {
try {
expectNoPendingImmediates()
followup()
resolve(pendingResult)
} catch (err) {
reject(err)
}
})
})
}
}
/**
* This is a utility function to make scheduling sequential tasks that run back to back easier.
* We schedule on the same queue (setTimeout) at the same time to ensure no other events can sneak in between.
* The function that runs in the second task gets access to the first tasks's result.
*/
export function pipelineInSequentialTasks<A, B, C>(
one: () => A,
two: (a: A) => B,
three: (b: B) => C
): Promise<C> {
if (process.env.NEXT_RUNTIME === 'edge') {
throw new InvariantError(
'`pipelineInSequentialTasks` should not be called in edge runtime.'
)
} else {
return new Promise((resolve, reject) => {
const scheduleTimeout = createAtomicTimerGroup()
let oneResult: A
scheduleTimeout(() => {
try {
DANGEROUSLY_runPendingImmediatesAfterCurrentTask()
oneResult = one()
} catch (err) {
clearTimeout(twoId)
clearTimeout(threeId)
clearTimeout(fourId)
reject(err)
}
})
let twoResult: B
const twoId = scheduleTimeout(() => {
// if `one` threw, then this timeout would've been cleared,
// so if we got here, we're guaranteed to have a value.
try {
DANGEROUSLY_runPendingImmediatesAfterCurrentTask()
twoResult = two(oneResult!)
} catch (err) {
clearTimeout(threeId)
clearTimeout(fourId)
reject(err)
}
})
let threeResult: C
const threeId = scheduleTimeout(() => {
// if `two` threw, then this timeout would've been cleared,
// so if we got here, we're guaranteed to have a value.
try {
expectNoPendingImmediates()
threeResult = three(twoResult!)
} catch (err) {
clearTimeout(fourId)
reject(err)
}
})
// We wait a task before resolving/rejecting
const fourId = scheduleTimeout(() => {
resolve(threeResult)
})
})
}
}
|