Skip to content

Commit fe8131c

Browse files
committed
Promises cancel recursively
1 parent 9e805b5 commit fe8131c

2 files changed

Lines changed: 44 additions & 1 deletion

File tree

src/api/flow.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,14 @@ export function createFlowGenerator(name: string, generator: Function) {
118118
const runId = ++generatorId
119119
const gen = action(`${name} - runid: ${runId} - init`, generator).apply(ctx, args)
120120
let rejector: (error: any) => void
121+
let pendingPromise: CancellablePromise<any> | undefined = undefined
121122

122123
const res = new Promise(function(resolve, reject) {
123124
let stepId = 0
124125
rejector = reject
125126

126127
function onFulfilled(res: any) {
128+
pendingPromise = undefined
127129
let ret
128130
try {
129131
ret = action(`${name} - runid: ${runId} - yield ${stepId++}`, gen.next).call(
@@ -138,6 +140,7 @@ export function createFlowGenerator(name: string, generator: Function) {
138140
}
139141

140142
function onRejected(err: any) {
143+
pendingPromise = undefined
141144
let ret
142145
try {
143146
ret = action(`${name} - runid: ${runId} - yield ${stepId++}`, gen.throw).call(
@@ -155,14 +158,17 @@ export function createFlowGenerator(name: string, generator: Function) {
155158
// TODO: support more type of values? See https://github.com/tj/co/blob/249bbdc72da24ae44076afd716349d2089b31c4c/index.js#L100
156159
if (!ret.value || typeof ret.value.then !== "function")
157160
return fail("Only promises can be yielded to asyncAction, got: " + ret)
158-
return ret.value.then(onFulfilled, onRejected)
161+
pendingPromise = ret.value
162+
return pendingPromise!.then(onFulfilled, onRejected)
159163
}
160164

161165
onFulfilled(undefined) // kick off the process
162166
}) as any
163167

164168
res.cancel = action(`${name} - runid: ${runId} - cancel`, function() {
165169
try {
170+
if (pendingPromise && typeof pendingPromise.cancel === "function")
171+
pendingPromise.cancel()
166172
gen.return()
167173
rejector(new Error("FLOW_CANCELLED"))
168174
} catch (e) {

test/base/flow.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,40 @@ test("flows can be cancelled - 5 - return before cancel", done => {
280280
)
281281
promise.cancel() // no-op
282282
})
283+
284+
test("flows can be cancelled - 5 - flows cancel recursively", done => {
285+
let flow1cancelled = false
286+
let flow2cancelled = false
287+
let stepsReached = 0
288+
289+
const flow1 = flow(function*() {
290+
try {
291+
yield Promise.resolve()
292+
stepsReached++
293+
} finally {
294+
flow1cancelled = true
295+
}
296+
})
297+
298+
const flow2 = flow(function*() {
299+
try {
300+
yield flow1()
301+
stepsReached++
302+
} finally {
303+
flow2cancelled = true
304+
}
305+
})
306+
307+
const p = flow2()
308+
p.then(
309+
() => fail(),
310+
err => {
311+
expect("" + err).toBe("Error: FLOW_CANCELLED")
312+
expect(stepsReached).toBe(0)
313+
expect(flow2cancelled).toBeTruthy()
314+
expect(flow1cancelled).toBeTruthy()
315+
done()
316+
}
317+
)
318+
p.cancel()
319+
})

0 commit comments

Comments
 (0)