@@ -30,21 +30,38 @@ public enum TaskState: String, StateType, Printable
3030 }
3131}
3232
33- public enum TaskEvent : String , StateEventType , Printable
33+ internal enum _TaskEvent : String , StateEventType , Printable
3434{
3535 case Pause = " Pause "
3636 case Resume = " Resume "
3737 case Progress = " Progress "
3838 case Fulfill = " Fulfill "
3939 case Reject = " Reject " // also used in cancellation for simplicity
40+
41+ //
42+ // Private events to temporarily switch states (no event handlers)
43+ // without invoking `configure.resume()`,
44+ // i.e.
45+ //
46+ // `.Paused` (paused-init)
47+ // => `.Running` (only while invoking `_performInitClosure()`)
48+ // => `.Paused` (switch back)
49+ //
50+ // and finally by sending regular `.Resume` event:
51+ //
52+ // => `.Running` (again, but this time `configure.resume()` will be invoked)
53+ //
54+ case _InitResume = " _InitResume "
55+ case _InitPause = " _InitPause "
56+
4057 case Any = " Any "
4158
42- public init ( nilLiteral: Void )
59+ internal init ( nilLiteral: Void )
4360 {
4461 self = Any
4562 }
4663
47- public var description : String
64+ internal var description : String
4865 {
4966 return self . rawValue
5067 }
@@ -83,7 +100,7 @@ public class Task<Progress, Value, Error>: Printable
83100 internal typealias _RejectHandler = ( ErrorInfo ) -> Void
84101 internal typealias _InitClosure = ( machine: Machine , progress: ProgressHandler , fulfill: FulFillHandler , _reject: _RejectHandler , configure: TaskConfiguration ) -> Void
85102
86- internal typealias Machine = StateMachine < TaskState , TaskEvent >
103+ internal typealias Machine = StateMachine < TaskState , _TaskEvent >
87104
88105 private var machine : Machine !
89106
@@ -245,6 +262,9 @@ public class Task<Progress, Value, Error>: Printable
245262 // setup state machine
246263 self . machine = Machine ( state: initialState) {
247264
265+ $0. addRouteEvent ( . _InitPause, transitions: [ . Running => . Paused] )
266+ $0. addRouteEvent ( . _InitResume, transitions: [ . Paused => . Running] )
267+
248268 $0. addRouteEvent ( . Pause, transitions: [ . Running => . Paused] )
249269 $0. addRouteEvent ( . Resume, transitions: [ . Paused => . Running] )
250270 $0. addRouteEvent ( . Progress, transitions: [ . Running => . Running] )
@@ -688,10 +708,37 @@ public class Task<Progress, Value, Error>: Printable
688708
689709 public func resume( ) -> Bool
690710 {
691- // always try `_performInitClosure` only once on `resume()`
692- // even when to-Resume-transition fails, e.g. already been fulfilled/rejected
693- self . _performInitClosure ? ( )
694- self . _performInitClosure = nil
711+ //
712+ // Always try `_performInitClosure` only once on `resume()`
713+ // even when `.Pause => .Resume` transition fails, e.g. already been fulfilled/rejected.
714+ //
715+ // NOTE:
716+ // **`downstream._performInitClosure` should be invoked first before `downstream.machine <-! .Resume`**
717+ // to add upstream's progress/fulfill/reject handlers inside `downstream.initClosure()`
718+ // before their actual calls, which often happens
719+ // when downstream's `resume()` is configured to call upstream's `resume()`
720+ // which eventually calls `upstream._performInitClosure` and thus actual event handlers.
721+ //
722+ if ( self . _performInitClosure != nil ) {
723+
724+ let isPaused = self . machine. state == . Paused
725+
726+ //
727+ // Temporarily switch to `.Running` without invoking `configure.resume()`.
728+ // This allows paused-inited-task to safely call progress/fulfill/reject handlers
729+ // inside its `initClosure` *immediately*.
730+ //
731+ if isPaused {
732+ self . machine <-! . _InitResume
733+ }
734+
735+ self . _performInitClosure ? ( )
736+ self . _performInitClosure = nil
737+
738+ if isPaused {
739+ self . machine <-! . _InitPause // switch back
740+ }
741+ }
695742
696743 return self . machine <-! . Resume
697744 }
0 commit comments