Skip to content

Commit e82a704

Browse files
committed
Merge pull request #17 from ReactKit/paused-init
Add paused-init feature.
2 parents f243381 + c926ab2 commit e82a704

File tree

1 file changed

+147
-97
lines changed

1 file changed

+147
-97
lines changed

SwiftTask/SwiftTask.swift

Lines changed: 147 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ public class TaskConfiguration
6565
}
6666
}
6767

68-
// abstract class for `weak _parentTask`
68+
// abstract class for `weak _parentTask` with arbitrary `Progress` & `Value` types
6969
public class _Task<Error>
7070
{
7171
internal weak var _parentTask: _Task?
7272

7373
internal let _weakified: Bool
7474

75-
public init(weakified: Bool) { self._weakified = weakified }
75+
public init(weakified: Bool, paused: Bool) { self._weakified = weakified }
7676
public func pause() -> Bool { return true }
7777
public func resume() -> Bool { return true }
7878
public func cancel(error: Error? = nil) -> Bool { return true }
@@ -98,11 +98,14 @@ public class Task<Progress, Value, Error>: _Task<Error>
9898

9999
internal typealias Machine = StateMachine<TaskState, TaskEvent>
100100

101-
internal var machine: Machine!
101+
private var machine: Machine!
102102

103103
// store initial parameters for cloning task when using `try()`
104104
internal var _initClosure: _InitClosure? // will be nil on fulfilled/rejected
105105

106+
/// wrapper closure for `_initClosure` to invoke only once when started `.Running`
107+
internal var _performInitClosure: (Void -> Void)?
108+
106109
/// progress value
107110
public internal(set) var progress: Progress?
108111

@@ -127,84 +130,109 @@ public class Task<Progress, Value, Error>: _Task<Error>
127130
}
128131

129132
///
130-
/// Creates new task.
133+
/// Creates a new task.
131134
///
132-
/// - e.g. Task<P, V, E>(weakified: false) { progress, fulfill, reject, configure in ... }
135+
/// - e.g. Task<P, V, E>(weakified: false, paused: false) { progress, fulfill, reject, configure in ... }
133136
///
134137
/// :param: weakified Weakifies progress/fulfill/reject handlers to let player (inner asynchronous implementation inside initClosure) NOT CAPTURE this created new task. Normally, weakified = false should be set to gain "player -> task" retaining, so that task will be automatically deinited when player is deinited. If weakified = true, task must be manually retained somewhere else, or it will be immediately deinited.
135138
///
139+
/// :param: paused Flag to invoke `initClosure` immediately or not. If `paused = true`, task's initial state will be `.Paused` and needs to `resume()` in order to start `.Running`. If `paused = false`, `initClosure` will be invoked immediately.
140+
///
136141
/// :param: initClosure e.g. { progress, fulfill, reject, configure in ... }. fulfill(value) and reject(error) handlers must be called inside this closure, where calling progress(progressValue) handler is optional. Also as options, configure.pause/resume/cancel closures can be set to gain control from outside e.g. task.pause()/resume()/cancel(). When using configure, make sure to use weak modifier when appropriate to avoid "task -> player" retaining which often causes retain cycle.
137142
///
138143
/// :returns: New task.
139144
///
140-
public init(weakified: Bool, initClosure: InitClosure)
145+
public init(weakified: Bool, paused: Bool, initClosure: InitClosure)
141146
{
142-
super.init(weakified: weakified)
147+
super.init(weakified: weakified, paused: paused)
143148

144149
let _initClosure: _InitClosure = { machine, progress, fulfill, _reject, configure in
145150
// NOTE: don't expose rejectHandler with ErrorInfo (isCancelled) for public init
146151
initClosure(progress: progress, fulfill: fulfill, reject: { (error: Error?) in _reject(ErrorInfo(error: error, isCancelled: false)) }, configure: configure)
147-
return
148152
}
149153

150-
self.setup(weakified, _initClosure)
154+
self.setup(weakified, paused: paused, _initClosure)
151155
}
152156

153-
/// creates task without weakifying progress/fulfill/reject handlers
157+
///
158+
/// creates a new task without weakifying progress/fulfill/reject handlers
159+
///
160+
/// - e.g. Task<P, V, E>(paused: false) { progress, fulfill, reject, configure in ... }
161+
///
162+
public convenience init(paused: Bool, initClosure: InitClosure)
163+
{
164+
self.init(weakified: false, paused: paused, initClosure: initClosure)
165+
}
166+
167+
///
168+
/// creates a new task without weakifying progress/fulfill/reject handlers (non-paused)
169+
///
170+
/// - e.g. Task<P, V, E> { progress, fulfill, reject, configure in ... }
171+
///
154172
public convenience init(initClosure: InitClosure)
155173
{
156-
self.init(weakified: false, initClosure: initClosure)
174+
self.init(weakified: false, paused: false, initClosure: initClosure)
157175
}
158176

159-
/// creates fulfilled task
177+
///
178+
/// creates fulfilled task (non-paused)
179+
///
180+
/// - e.g. Task<P, V, E>(value: someValue)
181+
///
160182
public convenience init(value: Value)
161183
{
162184
self.init(initClosure: { progress, fulfill, reject, configure in
163185
fulfill(value)
164-
return
165186
})
166187
}
167188

168-
/// creates rejected task
189+
///
190+
/// creates rejected task (non-paused)
191+
///
192+
/// - e.g. Task<P, V, E>(error: someError)
193+
///
169194
public convenience init(error: Error)
170195
{
171196
self.init(initClosure: { progress, fulfill, reject, configure in
172197
reject(error)
173-
return
174198
})
175199
}
176200

201+
///
177202
/// creates promise-like task which only allows fulfill & reject (no progress & configure)
203+
///
204+
/// - e.g. Task<Any, Value, Error> { fulfill, reject in ... }
205+
///
178206
public convenience init(promiseInitClosure: PromiseInitClosure)
179207
{
180208
self.init(initClosure: { progress, fulfill, reject, configure in
181209
promiseInitClosure(fulfill: fulfill, reject: { (error: Error) in reject(error) })
182-
return
183210
})
184211
}
185212

186-
/// NOTE: _initClosure has _RejectHandler as argument
187-
internal init(weakified: Bool = false, _initClosure: _InitClosure)
213+
/// internal-init for accessing private `machine` inside `_initClosure`
214+
/// (NOTE: _initClosure has _RejectHandler as argument)
215+
internal init(weakified: Bool = false, paused: Bool = false, _initClosure: _InitClosure)
188216
{
189-
super.init(weakified: weakified)
190-
self.setup(weakified, _initClosure)
217+
super.init(weakified: weakified, paused: paused)
218+
self.setup(weakified, paused: paused, _initClosure)
191219
}
192220

193-
internal func setup(weakified: Bool, _initClosure: _InitClosure)
221+
internal func setup(weakified: Bool, paused: Bool, _initClosure: _InitClosure)
194222
{
195223
// #if DEBUG
196224
// println("[init] \(self)")
197225
// #endif
198226

199-
self._initClosure = _initClosure
200-
201227
let configuration = Configuration()
202228

229+
let initialState: TaskState = paused ? .Paused : .Running
230+
203231
// NOTE: Swift 1.1 compiler fails if using [weak self] instead...
204232
weak var weakSelf = self
205233

206234
// setup state machine
207-
self.machine = Machine(state: .Running) {
235+
self.machine = Machine(state: initialState) {
208236

209237
$0.addRouteEvent(.Pause, transitions: [.Running => .Paused])
210238
$0.addRouteEvent(.Resume, transitions: [.Paused => .Running])
@@ -245,97 +273,114 @@ public class Task<Progress, Value, Error>: _Task<Error>
245273
// clear `_initClosure` & all StateMachine's handlers to prevent retain cycle
246274
$0.addEventHandler(.Fulfill, order: 255) { context in
247275
weakSelf?._initClosure = nil
276+
weakSelf?._performInitClosure = nil
248277
weakSelf?.machine?.removeAllHandlers()
249278
}
250279
$0.addEventHandler(.Reject, order: 255) { context in
251280
weakSelf?._initClosure = nil
281+
weakSelf?._performInitClosure = nil
252282
weakSelf?.machine?.removeAllHandlers()
253283
}
254284

255285
}
256286

257-
var progressHandler: ProgressHandler
258-
var fulfillHandler: FulFillHandler
259-
var rejectHandler: _RejectHandler
287+
self._initClosure = _initClosure
260288

261-
if weakified {
262-
progressHandler = { [weak self] (progress: Progress) in
263-
if let self_ = self {
264-
let oldProgress = self_.progress
265-
self_.machine <-! (.Progress, (oldProgress, progress))
266-
}
267-
}
289+
// will be invoked only once
290+
self._performInitClosure = { [weak self] in
268291

269-
fulfillHandler = { [weak self] (value: Value) in
270-
if let self_ = self {
271-
self_.machine <-! (.Fulfill, value)
292+
if let self_ = self {
293+
294+
var progressHandler: ProgressHandler
295+
var fulfillHandler: FulFillHandler
296+
var rejectHandler: _RejectHandler
297+
298+
if weakified {
299+
progressHandler = { [weak self_] (progress: Progress) in
300+
if let self_ = self_ {
301+
let oldProgress = self_.progress
302+
self_.machine <-! (.Progress, (oldProgress, progress))
303+
}
304+
}
305+
306+
fulfillHandler = { [weak self_] (value: Value) in
307+
if let self_ = self_ {
308+
self_.machine <-! (.Fulfill, value)
309+
}
310+
}
311+
312+
rejectHandler = { [weak self_] (errorInfo: ErrorInfo) in
313+
if let self_ = self_ {
314+
self_.machine <-! (.Reject, errorInfo)
315+
}
316+
}
272317
}
273-
}
274-
275-
rejectHandler = { [weak self] (errorInfo: ErrorInfo) in
276-
if let self_ = self {
277-
self_.machine <-! (.Reject, errorInfo)
318+
else {
319+
progressHandler = { (progress: Progress) in
320+
let oldProgress = self_.progress
321+
self_.machine <-! (.Progress, (oldProgress, progress))
322+
return
323+
}
324+
325+
fulfillHandler = { (value: Value) in
326+
self_.machine <-! (.Fulfill, value)
327+
return
328+
}
329+
330+
rejectHandler = { (errorInfo: ErrorInfo) in
331+
self_.machine <-! (.Reject, errorInfo)
332+
return
333+
}
278334
}
279-
}
280-
}
281-
else {
282-
progressHandler = { (progress: Progress) in
283-
let oldProgress = self.progress
284-
self.machine <-! (.Progress, (oldProgress, progress))
285-
return
286-
}
287335

288-
fulfillHandler = { (value: Value) in
289-
self.machine <-! (.Fulfill, value)
290-
return
291-
}
292-
293-
rejectHandler = { (errorInfo: ErrorInfo) in
294-
self.machine <-! (.Reject, errorInfo)
295-
return
296-
}
297-
}
298-
299-
_initClosure(machine: self.machine, progress: progressHandler, fulfill: fulfillHandler, _reject: rejectHandler, configure: configuration)
300-
301-
let userPauseClosure = configuration.pause
302-
let userResumeClosure = configuration.resume
303-
let userCancelClosure = configuration.cancel
304-
305-
// add parentTask-pause/resume/cancel functionalities after retrieving user-defined configuration
306-
configuration.pause = { [weak self] in
307-
userPauseClosure?()
308-
309-
var task: _Task? = self
310-
while let parentTask = task?._parentTask {
311-
if parentTask._weakified { break }
336+
_initClosure(machine: self_.machine, progress: progressHandler, fulfill: fulfillHandler, _reject: rejectHandler, configure: configuration)
312337

313-
parentTask.pause()
314-
task = parentTask
315-
}
316-
317-
}
318-
configuration.resume = { [weak self] in
319-
userResumeClosure?()
320-
321-
var task: _Task? = self
322-
while let parentTask = task?._parentTask {
323-
if parentTask._weakified { break }
338+
let userPauseClosure = configuration.pause
339+
let userResumeClosure = configuration.resume
340+
let userCancelClosure = configuration.cancel
324341

325-
parentTask.resume()
326-
task = parentTask
327-
}
328-
}
329-
configuration.cancel = { [weak self] in
330-
userCancelClosure?()
331-
332-
var task: _Task? = self
333-
while let parentTask = task?._parentTask {
334-
if parentTask._weakified { break }
342+
// add parentTask-pause/resume/cancel functionalities after retrieving user-defined configuration
343+
configuration.pause = { [weak self_] in
344+
userPauseClosure?()
345+
346+
var task: _Task? = self_
347+
while let parentTask = task?._parentTask {
348+
if parentTask._weakified { break }
349+
350+
parentTask.pause()
351+
task = parentTask
352+
}
353+
354+
}
355+
configuration.resume = { [weak self_] in
356+
userResumeClosure?()
357+
358+
var task: _Task? = self_
359+
while let parentTask = task?._parentTask {
360+
if parentTask._weakified { break }
361+
362+
parentTask.resume()
363+
task = parentTask
364+
}
365+
}
366+
configuration.cancel = { [weak self_] in
367+
userCancelClosure?()
368+
369+
var task: _Task? = self_
370+
while let parentTask = task?._parentTask {
371+
if parentTask._weakified { break }
372+
373+
parentTask.cancel()
374+
task = parentTask
375+
}
376+
}
335377

336-
parentTask.cancel()
337-
task = parentTask
338378
}
379+
380+
}
381+
382+
if !paused {
383+
self.resume()
339384
}
340385
}
341386

@@ -672,6 +717,11 @@ public class Task<Progress, Value, Error>: _Task<Error>
672717

673718
public override func resume() -> Bool
674719
{
720+
// always try `_performInitClosure` only once on `resume()`
721+
// even when to-Resume-transition fails, e.g. already been fulfilled/rejected
722+
self._performInitClosure?()
723+
self._performInitClosure = nil
724+
675725
return self.machine <-! .Resume
676726
}
677727

0 commit comments

Comments
 (0)