Skip to content

Commit 7570ac0

Browse files
committed
Merge pull request #10 from ReactKit/async-chained-pause-cancel
Fix pause/cancel bug when chained with then/success/catch.
2 parents 9438564 + d489ef9 commit 7570ac0

File tree

5 files changed

+531
-82
lines changed

5 files changed

+531
-82
lines changed

SwiftTask/SwiftTask.swift

Lines changed: 127 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,18 @@ public class TaskConfiguration
6565
}
6666
}
6767

68-
public class Task<Progress, Value, Error>
68+
// abstract class for `weak _parentTask`
69+
public class _Task<Error>
70+
{
71+
internal weak var _parentTask: _Task?
72+
73+
public init() {}
74+
public func pause() -> Bool { return true }
75+
public func resume() -> Bool { return true }
76+
public func cancel(error: Error? = nil) -> Bool { return true }
77+
}
78+
79+
public class Task<Progress, Value, Error>: _Task<Error>
6980
{
7081
public typealias ErrorInfo = (error: Error?, isCancelled: Bool)
7182

@@ -81,7 +92,7 @@ public class Task<Progress, Value, Error>
8192
public typealias InitClosure = (progress: ProgressHandler, fulfill: FulFillHandler, reject: RejectHandler, configure: TaskConfiguration) -> Void
8293

8394
internal typealias _RejectHandler = (ErrorInfo) -> Void
84-
internal typealias _InitClosure = (progress: ProgressHandler, fulfill: FulFillHandler, _reject: _RejectHandler, configure: TaskConfiguration) -> Void
95+
internal typealias _InitClosure = (machine: Machine, progress: ProgressHandler, fulfill: FulFillHandler, _reject: _RejectHandler, configure: TaskConfiguration) -> Void
8596

8697
internal typealias Machine = StateMachine<TaskState, TaskEvent>
8798

@@ -90,7 +101,7 @@ public class Task<Progress, Value, Error>
90101
// store initial parameters for cloning task when using `try()`
91102
internal let _weakified: Bool
92103
internal var _initClosure: _InitClosure? // will be nil on fulfilled/rejected
93-
104+
94105
/// progress value
95106
public internal(set) var progress: Progress?
96107

@@ -127,12 +138,13 @@ public class Task<Progress, Value, Error>
127138
public init(weakified: Bool, initClosure: InitClosure)
128139
{
129140
self._weakified = weakified
130-
self._initClosure = { (progress, fulfill, _reject: _RejectHandler, configure) in
141+
self._initClosure = { machine, progress, fulfill, _reject, configure in
131142
// NOTE: don't expose rejectHandler with ErrorInfo (isCancelled) for public init
132143
initClosure(progress: progress, fulfill: fulfill, reject: { (error: Error?) in _reject(ErrorInfo(error: error, isCancelled: false)) }, configure: configure)
133144
return
134145
}
135146

147+
super.init()
136148
self.setup(weakified, self._initClosure!)
137149
}
138150

@@ -175,14 +187,15 @@ public class Task<Progress, Value, Error>
175187
self._weakified = weakified
176188
self._initClosure = _initClosure
177189

190+
super.init()
178191
self.setup(weakified, _initClosure)
179192
}
180193

181194
internal func setup(weakified: Bool, _initClosure: _InitClosure)
182195
{
183-
#if DEBUG
184-
println("[init] \(self)")
185-
#endif
196+
// #if DEBUG
197+
// println("[init] \(self)")
198+
// #endif
186199

187200
let configuration = Configuration()
188201

@@ -227,14 +240,14 @@ public class Task<Progress, Value, Error>
227240
configuration.clear()
228241
}
229242

230-
// clear `_initClosure` after fulfilled/rejected to prevent retain cycle
243+
// clear `_initClosure` & all StateMachine's handlers to prevent retain cycle
231244
$0.addEventHandler(.Fulfill, order: 255) { context in
232245
weakSelf?._initClosure = nil
233-
return
246+
weakSelf?.machine?.removeAllHandlers()
234247
}
235248
$0.addEventHandler(.Reject, order: 255) { context in
236249
weakSelf?._initClosure = nil
237-
return
250+
weakSelf?.machine?.removeAllHandlers()
238251
}
239252

240253
}
@@ -281,7 +294,41 @@ public class Task<Progress, Value, Error>
281294
}
282295
}
283296

284-
_initClosure(progress: progressHandler, fulfill: fulfillHandler, _reject: rejectHandler, configure: configuration)
297+
_initClosure(machine: self.machine, progress: progressHandler, fulfill: fulfillHandler, _reject: rejectHandler, configure: configuration)
298+
299+
let userPauseClosure = configuration.pause
300+
let userResumeClosure = configuration.resume
301+
let userCancelClosure = configuration.cancel
302+
303+
// add parentTask-pause/resume/cancel functionalities after retrieving user-defined configuration
304+
configuration.pause = { [weak self] in
305+
userPauseClosure?()
306+
307+
var task: _Task? = self
308+
while let parentTask = task?._parentTask {
309+
parentTask.pause()
310+
task = parentTask
311+
}
312+
313+
}
314+
configuration.resume = { [weak self] in
315+
userResumeClosure?()
316+
317+
var task: _Task? = self
318+
while let parentTask = task?._parentTask {
319+
parentTask.resume()
320+
task = parentTask
321+
}
322+
}
323+
configuration.cancel = { [weak self] in
324+
userCancelClosure?()
325+
326+
var task: _Task? = self
327+
while let parentTask = task?._parentTask {
328+
parentTask.cancel()
329+
task = parentTask
330+
}
331+
}
285332

286333
}
287334

@@ -339,9 +386,9 @@ public class Task<Progress, Value, Error>
339386
/// then (fulfilled & rejected) + closure returning task
340387
public func then<Progress2, Value2>(thenClosure: (Value?, ErrorInfo?) -> Task<Progress2, Value2, Error>) -> Task<Progress2, Value2, Error>
341388
{
342-
let newTask = Task<Progress2, Value2, Error> { (progress, fulfill, _reject: _RejectHandler, configure) in
389+
let newTask = Task<Progress2, Value2, Error> { machine, progress, fulfill, _reject, configure in
343390

344-
let bind = { (value: Value?, errorInfo: ErrorInfo?) -> Void in
391+
let bind = { [weak machine] (value: Value?, errorInfo: ErrorInfo?) -> Void in
345392
let innerTask = thenClosure(value, errorInfo)
346393

347394
// NOTE: don't call `then` for innerTask, or recursive bindings may occur
@@ -352,6 +399,11 @@ public class Task<Progress, Value, Error>
352399
case .Rejected:
353400
_reject(innerTask.errorInfo!)
354401
default:
402+
innerTask.machine.addEventHandler(.Progress) { context in
403+
if let (_, progressValue) = context.userInfo as? Task<Progress2, Value2, Error>.ProgressTuple {
404+
progress(progressValue)
405+
}
406+
}
355407
innerTask.machine.addEventHandler(.Fulfill) { context in
356408
if let value = context.userInfo as? Value2 {
357409
fulfill(value)
@@ -367,6 +419,14 @@ public class Task<Progress, Value, Error>
367419
configure.pause = { innerTask.pause(); return }
368420
configure.resume = { innerTask.resume(); return }
369421
configure.cancel = { innerTask.cancel(); return }
422+
423+
// pause/cancel innerTask if descendant task is already paused/cancelled
424+
if machine!.state == .Paused {
425+
innerTask.pause()
426+
}
427+
else if machine!.state == .Cancelled {
428+
innerTask.cancel()
429+
}
370430
}
371431

372432
switch self.machine.state {
@@ -375,6 +435,11 @@ public class Task<Progress, Value, Error>
375435
case .Rejected:
376436
bind(nil, self.errorInfo!)
377437
default:
438+
self.machine.addEventHandler(.Progress) { context in
439+
if let (_, progressValue) = context.userInfo as? Task<Progress2, Value2, Error>.ProgressTuple {
440+
progress(progressValue)
441+
}
442+
}
378443
self.machine.addEventHandler(.Fulfill) { context in
379444
if let value = context.userInfo as? Value {
380445
bind(value, nil)
@@ -389,6 +454,8 @@ public class Task<Progress, Value, Error>
389454

390455
}
391456

457+
newTask._parentTask = self
458+
392459
return newTask
393460
}
394461

@@ -403,12 +470,14 @@ public class Task<Progress, Value, Error>
403470
/// success (fulfilled) + closure returning task
404471
public func success<Progress2, Value2>(successClosure: Value -> Task<Progress2, Value2, Error>) -> Task<Progress2, Value2, Error>
405472
{
406-
let newTask = Task<Progress2, Value2, Error> { (progress, fulfill, _reject: _RejectHandler, configure) in
473+
let newTask = Task<Progress2, Value2, Error> { machine, progress, fulfill, _reject, configure in
407474

408-
let bind = { (value: Value) -> Void in
475+
let bind = { [weak machine] (value: Value) -> Void in
409476
let innerTask = successClosure(value)
410477

411-
innerTask.then { (value: Value2?, errorInfo: ErrorInfo?) -> Void in
478+
innerTask.progress { _, progressValue in
479+
progress(progressValue)
480+
}.then { (value: Value2?, errorInfo: ErrorInfo?) -> Void in
412481
if let value = value {
413482
fulfill(value)
414483
}
@@ -420,6 +489,14 @@ public class Task<Progress, Value, Error>
420489
configure.pause = { innerTask.pause(); return }
421490
configure.resume = { innerTask.resume(); return }
422491
configure.cancel = { innerTask.cancel(); return }
492+
493+
// pause/cancel innerTask if descendant task is already paused/cancelled
494+
if machine!.state == .Paused {
495+
innerTask.pause()
496+
}
497+
else if machine!.state == .Cancelled {
498+
innerTask.cancel()
499+
}
423500
}
424501

425502
switch self.machine.state {
@@ -428,6 +505,11 @@ public class Task<Progress, Value, Error>
428505
case .Rejected:
429506
_reject(self.errorInfo!)
430507
default:
508+
self.machine.addEventHandler(.Progress) { context in
509+
if let (_, progressValue) = context.userInfo as? Task<Progress2, Value2, Error>.ProgressTuple {
510+
progress(progressValue)
511+
}
512+
}
431513
self.machine.addEventHandler(.Fulfill) { context in
432514
if let value = context.userInfo as? Value {
433515
bind(value)
@@ -442,6 +524,8 @@ public class Task<Progress, Value, Error>
442524

443525
}
444526

527+
newTask._parentTask = self
528+
445529
return newTask
446530
}
447531

@@ -456,12 +540,14 @@ public class Task<Progress, Value, Error>
456540
/// failure (rejected) + closure returning task
457541
public func failure(failureClosure: ErrorInfo -> Task) -> Task
458542
{
459-
let newTask = Task { (progress, fulfill, _reject: _RejectHandler, configure) in
543+
let newTask = Task { machine, progress, fulfill, _reject, configure in
460544

461-
let bind = { (errorInfo: ErrorInfo) -> Void in
545+
let bind = { [weak machine] (errorInfo: ErrorInfo) -> Void in
462546
let innerTask = failureClosure(errorInfo)
463547

464-
innerTask.then { (value: Value?, errorInfo: ErrorInfo?) -> Void in
548+
innerTask.progress { _, progressValue in
549+
progress(progressValue)
550+
}.then { (value: Value?, errorInfo: ErrorInfo?) -> Void in
465551
if let value = value {
466552
fulfill(value)
467553
}
@@ -472,7 +558,15 @@ public class Task<Progress, Value, Error>
472558

473559
configure.pause = { innerTask.pause(); return }
474560
configure.resume = { innerTask.resume(); return }
475-
configure.cancel = { innerTask.cancel(); return}
561+
configure.cancel = { innerTask.cancel(); return }
562+
563+
// pause/cancel innerTask if descendant task is already paused/cancelled
564+
if machine!.state == .Paused {
565+
innerTask.pause()
566+
}
567+
else if machine!.state == .Cancelled {
568+
innerTask.cancel()
569+
}
476570
}
477571

478572
switch self.machine.state {
@@ -482,6 +576,11 @@ public class Task<Progress, Value, Error>
482576
let errorInfo = self.errorInfo!
483577
bind(errorInfo)
484578
default:
579+
self.machine.addEventHandler(.Progress) { context in
580+
if let (_, progressValue) = context.userInfo as? Task.ProgressTuple {
581+
progress(progressValue)
582+
}
583+
}
485584
self.machine.addEventHandler(.Fulfill) { context in
486585
if let value = context.userInfo as? Value {
487586
fulfill(value)
@@ -496,20 +595,22 @@ public class Task<Progress, Value, Error>
496595

497596
}
498597

598+
newTask._parentTask = self
599+
499600
return newTask
500601
}
501602

502-
public func pause() -> Bool
603+
public override func pause() -> Bool
503604
{
504605
return self.machine <-! .Pause
505606
}
506607

507-
public func resume() -> Bool
608+
public override func resume() -> Bool
508609
{
509610
return self.machine <-! .Resume
510611
}
511612

512-
public func cancel(error: Error? = nil) -> Bool
613+
public override func cancel(error: Error? = nil) -> Bool
513614
{
514615
return self._cancel(error: error)
515616
}
@@ -524,7 +625,7 @@ extension Task
524625
{
525626
public class func all(tasks: [Task]) -> Task<BulkProgress, [Value], Error>
526627
{
527-
return Task<BulkProgress, [Value], Error> { (progress, fulfill, _reject: _RejectHandler, configure) in
628+
return Task<BulkProgress, [Value], Error> { machine, progress, fulfill, _reject, configure in
528629

529630
var completedCount = 0
530631
let totalCount = tasks.count
@@ -570,7 +671,7 @@ extension Task
570671

571672
public class func any(tasks: [Task]) -> Task
572673
{
573-
return Task<Progress, Value, Error> { (progress, fulfill, _reject: _RejectHandler, configure) in
674+
return Task<Progress, Value, Error> { machine, progress, fulfill, _reject, configure in
574675

575676
var completedCount = 0
576677
var rejectedCount = 0
@@ -615,7 +716,7 @@ extension Task
615716
/// This new task will NEVER be internally rejected.
616717
public class func some(tasks: [Task]) -> Task<BulkProgress, [Value], Error>
617718
{
618-
return Task<BulkProgress, [Value], Error> { (progress, fulfill, _reject: _RejectHandler, configure) in
719+
return Task<BulkProgress, [Value], Error> { machine, progress, fulfill, _reject, configure in
619720

620721
var completedCount = 0
621722
let totalCount = tasks.count

0 commit comments

Comments
 (0)