PromiseKit firstlyOn style method - swift

PromiseKit provides a convenience method thenOn for running parts of your chain on non-main threads. However there doesn't appear to be any convenient way of setting the first promise execution thread.
This means that I end up either placing a DispatchQueue.global(x).async inside my first promise, or I use a dummy first promise.
Placing the DispatchQueue bit in my first promise feels broken, I'm moving the threading decision from the main execution chain to the individual promise, but just for that one promise. If I later prepend a promise to my chain, I have to move all that threading logic around... not good.
What i've been doing lately is this:
let queue = DispatchQueue.global(qos: .userInitiated)
Promise(value: ()).then(on: queue) {
// Now run first promise function
}
This is definitely a cleaner solution, but I was wondering if anyone knew of an
even better solution... I'm sure this isn't a rare scenario after all?

We provide:
DispatchQueue.global().promise {
//…
}
firstly does not take a queue because it executes its contents immediately for the default case and thus allowing it to have a configured queue would make its behavior confusingly variable.
UPDATE: At the latest version it looks like
DispatchQueue.global().async(.promise) {
//...
}

You can perform your operation on any queue, just call fulfill or reject from it
Look at example:
override func viewDidLoad() {
super.viewDidLoad()
firstly {
doFirstly()
}
.then {
self.doMain()
}
.then {
self.doLastest()
}
.catch { error in
// handle error
}
.always {
// finish
}
}
func doFirstly() -> Promise<Void> {
return Promise { fulfill, reject in
let queue = DispatchQueue.global(qos: .userInitiated)
queue.async {
// do your operation here
fulfill()
}
}
}
func doMain() -> Promise<Void> {
return Promise { fulfill, reject in
let queue = DispatchQueue.main
queue.async {
// do your operation here
fulfill()
}
}
}
func doLastest() -> Promise<Void> {
return Promise { fulfill, reject in
let queue = DispatchQueue.global(qos: .userInitiated)
queue.async {
// do your operation here
fulfill()
}
}
}

Related

Behavior for receive(on:) for DispatchQueue.main

Given the code below from a class:
cancellable = input
.receive(on: scheduler)
.map { ... }
.sink(receiveValue: { value in
self.state = value
})
where input is a PassthroughSubject.
Now, when scheduler is the main queue or the RunLoop.main AND input will be called from the main thread, does receive(on: scheduler) programmatically optimise away an explicit dispatch to the main queue?
So, basically something like this:
if Thread.isMainThread {
/* execute closure */
} else {
/* dispatch closure async to main */
}
The documentation for receive(on:) gives a vague hint, that it might perform some optimisations:
"Prefer receive(on:options:) over explicit use of dispatch queues"
pub.sink {
DispatchQueue.main.async {
// Do something.
}
}
No, receive(on:) does not optimize away the dispatch. Doing so could lead to a deadlock. Example:
let l = Lock()
let cancellable = input
.receive(on: scheduler)
.map { ... }
.sink(receiveValue: { value in
l.lock()
self.state = value
l.unlock()
})
l.lock()
input.send(1)
l.unlock()
If the dispatch were eliminated, this example would try to lock the already-locked lock l, and hang (or crash if it can detect the deadlock).
Looking at the sources for RunLoop and DispatchQueue, it doesn't look like there is such an optimisation in their conformance to the Scheduler protocol.
But to be fair, there might be lower level optimisations at play.

Using Dispatch.main in code called from XCTestCase does not work

I have a function that is a async wrapper around a synchronous function.
The synchronous function is like:
class Foo {
class func bar() -> [Int] {
return [1,2,3]
}
class func asyncBar(completion: #escaping ([Int]) -> Void) {
DispatchQueue.global(qos: .userInitiated).async {
let intArray = bar()
DispatchQueue.main.async {
completion(intArray)
}
}
}
}
When I call it from a XCTestCase completion does not run.
Is there some sort of perverse interaction between the way unit tests are done in XCode and the main thread?
I can find no documentation on the Web about this.
I have to use the main thread for the callback as it interacts with the Gui.
My test case looks something like:
func testAsyncBar() throws {
var run = true
func stopThisThing(ints: [Int]) {
run = false
}
Foo.asyncBar(completion: stopThisThing)
while run {
print ("Running...")
usleep(100000)
}
}
The busy loop at the end never stops.
Your test’s while loop will block the main thread (if the test runs on the main thread). As such, the closure dispatched via DispatchQueue.main.async can never run, and your run Boolean will never get reset. This results in a deadlock. You can confirm this by printing Thread.isMainThread or adding a test like dispatchPrecondition(condition: .onQueue(.main)).
Fortunately, unit tests have a simple mechanism that avoids this deadlock.
If you want unit test to wait for some asynchronous process, use an XCTestExpectation:
func testAsyncBar() throws {
let e = expectation(description: "asyncBar")
func stopThisThing(ints: [Int]) {
e.fulfill()
}
Foo.asyncBar(completion: stopThisThing)
waitForExpectations(timeout: 5)
}
This avoids problems introduced by the while loop that otherwise blocked the thread.
See Testing Asynchronous Operations with Expectations.

Swift: Thread safe Singleton, why do we use sync for read?

While making a thread-safe Singleton, it is advised to use a sync for read and an async with a barrier for write operation.
My question is why do we use a sync for read? What might happen if we perform a read with async operation?
Here is an example of what is recommended:
func getUser(id: String) throws -> User {
var user: User!
try concurrentQueue.sync {
user = try storage.getUser(id)
}
return user
}
func setUser(_ user: User, completion: (Result<()>) -> Void) {
try concurrentQueue.async(flags: .barrier) {
do {
try storage.setUser(user)
completion(.value(())
} catch {
completion(.error(error))
}
}
}
The concept of using concurrent queue with “read concurrently with sync; write with barrier with async” is a very common synchronization pattern called “reader-writer”. The idea is that the concurrent queue is just for synchronizing writes with a barrier, but that reads will take place concurrently with respect to other reads.
So, here’s a simple, real-world example of using reader-writer for synchronized access to some private state property:
enum State {
case notStarted
case running
case complete
}
class ComplexProcessor {
private var readerWriterQueue = DispatchQueue(label: "...", attributes: .concurrent)
// private backing stored property
private var _state: State = .notStarted
// exposed computed property synchronizes access using reader-writer pattern
var state: State {
get { readerWriterQueue.sync { _state } }
set { readerWriterQueue.async { self._state = newValue } }
}
func start() {
state = .running
DispatchQueue.global().async {
// do something complicated here
self.state = .complete
}
}
}
Consider:
let processor = ComplexProcessor()
processor.start()
And then, later:
if processor.state == .complete {
...
}
The state computed property is using the reader-writer pattern to offer thread-safe access to the underlying stored property. It synchronizes access to some memory location, and we are confident that it will be responsive. In this case, we don’t need confusing #escaping closures: The sync reads result in very simple code that is easy to reason about.
That having been said, in your example, you’re not just synchronizing interaction with some property, but synchronizing the interaction with storage. If that’s local storage that is guaranteed to be responsive, then the reader-writer pattern is probably fine.
But if storage methods could take anything more than a few milliseconds to run, you wouldn’t want to use the reader-writer pattern. The fact that getUser can throw errors makes me wonder if storage is already doing complicated processing. And even if it is just reading quickly from some local store, what if it was later refactored to interact with some remote store, subject to unknown network latency/issues? Bottom line, it is questionable to have the getUser method making assumptions about implementation details of storage, assuming that the value will always be returned quickly.
In that case, you would refactor getUser method to use #escaping completion handler closure, as suggested by Jeffery Thomas. We never want to have a synchronous method that might take more than a few milliseconds, because we never want to block the calling thread (especially if it’s the main thread).
By the way, if you stay with reader-writer pattern, you can simplify your getUser, because sync returns whatever value its closure returns:
func getUser(id: String) throws -> User {
return try concurrentQueue.sync {
try storage.getUser(id)
}
}
And you can’t use try in conjunction with async (only within your do-catch block). So it’s just:
func setUser(_ user: User, completion: (Result<()>) -> Void) {
concurrentQueue.async(flags: .barrier) {
do {
try storage.setUser(user)
completion(.value(())
} catch {
completion(.error(error))
}
}
}
It's all in what you want. By changing get user to async, then you need to use a callback to wait for the value.
func getUser(id: String, completion: #escaping (Result<User>) -> Void) -> Void {
concurrentQueue.async {
do {
let user = try storage.getUser(id)
completion(.value(user))
} catch {
completion(.error(error))
}
}
}
func setUser(_ user: User, completion: #escaping (Result<()>) -> Void) {
concurrentQueue.async(flags: .barrier) {
do {
try storage.setUser(user)
completion(.value(()))
} catch {
completion(.error(error))
}
}
}
That changes the API of get user, so now when calling get user, a callback will need to be used.
Instead of somethings like this
do {
let user = try manager.getUser(id: "test")
updateUI(user: user)
} catch {
handleError(error)
}
you will need something like this
manager.getUser(id: "test") { [weak self] result in
switch result {
case .value(let user): self?.updateUI(user: user)
case .error(let error): self?.handleError(error)
}
}
Assuming you have somethings like a view controller with a property named manager and methods updateUI() and handleError()

How to set up when(fulfilled:) in PromiseKit?

I have a function set up to return a Promise<PFObject>. I would like to use this function in PromiseKit's when(fulfilled:) functionality, but whenever I try to do so, I get an error. Here is the function which returns the Promise<PFObject>:
func Query() -> Promise<PFObject>{
return Promise{ fulfill, reject in
let linkQueryy = PFUser.query()
linkQueryy?.findObjectsInBackground(block: { (objectss, error) in
if let objects = objectss{
for object in objects{
fulfill(object)
}
}
})
}
}
As you can see, the function returns the Promise upon fulfillment. Thus, I tried to set up a when statement in my viewDidLoad() as follows:
override func viewDidLoad() {
super.viewDidLoad()
when(fulfilled: Query()).then{
//do more asynch stuff
}
}
However, I get the error that xcode cannot "invoke 'when' with an argument list type of '(fulfilled: Promise<PFObject>)'". I do not know how to fix this as I thought I had it set up correctly. The when needs a promise, and I am giving it one so I am not sure what to do.
Try as follows :
when(fulfilled: [linkQueryy()] as! [Promise<Any>]).then { _ in
// do more asynch stuff
}
The parameter fulfilled: needs to be an iterable.
By the way, when(fulfilled:) is necessary only when you have many promises and need wait for all to complete successfully. But in your code, you need to wait for only one promise.
For a single promise, the better way is to form a chain as follows :
firstly {
linkQueryy()
}.then { _ -> Void in
// do more asynch stuff
}.catch { _ in
// error!
}

Can Swift return value from an async Void-returning block?

I want to create a function to check if user_id is already in my database.
class func checkIfUserExsits(uid:String) -> Bool {
userRef.childByAppendingPath(uid).observeSingleEventOfType(.Value, withBlock: { (snapShot: FDataSnapshot!) -> Void in
if snapShot.value is NSNull {
return false
} else {
return true
}
})
}
However, observeSingleEventOfType is a API provided by 3rd party Firebase. It is defined to return Void.
(void)observeSingleEventOfType:(FEventType)eventType withBlock:(void ( ^ ) ( FDataSnapshot *snapshot ))block
Error: Type 'Void' does not conform to protocol 'BooleanLiteralConvertible'
Appreciate any kind of helps.
UPDATE
I am trying a different way:
class func checkIfExist(uid: String) -> Bool {
var answer:Bool = false
var text:String = "not yet completed"
let queue = dispatch_group_create()
dispatch_group_enter(queue)
userRef.childByAppendingPath(uid).observeSingleEventOfType(.Value, withBlock: { (snapShot: FDataSnapshot!) -> Void in
if snapShot.value is NSNull {
text = "This is a new user"
answer = false
dispatch_group_leave(queue)
} else {
text = "Found the user in Firebase"
answer = true
dispatch_group_leave(queue)
}
})
dispatch_group_wait(queue, DISPATCH_TIME_FOREVER)
println(text)
return answer
}
Somehow it just freeze there. I know this approach could be off-topic now. But please help.
You should employ asynchronous (ie, escaping) completion handler yourself:
class func checkIfUserExists(uid: String, completion: #escaping (Bool) -> Void) {
userRef.childByAppendingPath(uid).observeSingleEventOfType(.Value) { snapShot in
if snapShot.value is NSNull {
completion(false)
} else {
completion(true)
}
}
}
You can then call this like so:
MyClass.checkIfUserExists(uid) { success in
// use success here
}
// but not here
In your revised question, you demonstrate the use of dispatch groups to make this asynchronous method behave synchronously. (Semaphores are also often used to the same ends.)
Two issues:
This will deadlock if they dispatch their completion handler back to the main queue (and in many cases, libraries will do this to simplify life for us), because you're coincidentally blocking the very same thread they're trying to use. I don't know if that's what they've done here, but is likely.
If you want to confirm this, temporarily remove dispatch group and then examine NSThread.isMainThread and see if it's running in main thread or not.
You never should block the main thread, anyway. They provided an asynchronous interface for good reason, so you should use asynchronous patterns when calling it. Don't fight the asynchronous patterns, but rather embrace them.