"await" results of tasks in Swift - asynchronous

I come from a C# background and would like to implement awaiting functionality in my Swift app. I've achieved my desired results but I had to use a semaphore which I'm not sure is good practice. I have a function with an alamo request that returns a JSON with a success value and as I understand it that request function is async with a completion handler. The handler fires once the request is complete. The problem is returning the success value from that operation. Here's a psuedo-code example of what I'm doing:
func AlamoTest() -> Bool{
var success = false
//Do some things...
//...
//Signal from async code
let semaphore = DispatchSemaphore(value: 0)
Alamofire.request("blah blah blah", method: .post, parameters: parameters, encoding: URLEncoding.default).responseJSON { response in {
success = response["success"]
if(success){
//Do some more things
}
semaphore.signal() //Signal async code is done
}
//Wait until async code done to get result
semaphore.wait(timeout: DispatchTime.distantFuture)
return success
}
Is there a "better" way of achieving my goal? I'm new to Swift and its async constructs.

Best solution I found is what I call "callback chaining". Example of my method looks like this:
func postJSON(json: NSDictionary, function: ServerFunction, completionHandler: ((_ jsonResponse: NSDictionary) -> Void)? = nil) {
//Create json payload from dictionary object
guard let payload = serializeJSON(json: json) else {
print("Error creating json from json parameter")
return
}
//Send request
Alamofire.request(urlTemplate(function.rawValue), method: .post, parameters: payload, encoding: URLEncoding.default).validate().responseJSON { response in
//Check response from server
switch response.result {
case .success(let data):
let jsonResponse = data as! NSDictionary
print("\(jsonResponse)")
//Execute callback post request handler
if completionHandler != nil {
completionHandler!(jsonResponse)
}
case .failure(let error):
print("Shit didn't work!\(error)")
}
}
}
The last parameter is a closure that executes once the orginal async operation is complete. You pass in the result to the closure and do what you want with it. In my case I wanted to disable the view while the async operations were rolling. You can enable the view in your closure argument since the result from the alamo async operation is called on the main thread. completionHandler defaults to nil if you don't need the result and stops the chaining.

You can use this framework for Swift coroutines - https://github.com/belozierov/SwiftCoroutine
func AlamoTest() throws -> Bool {
try Coroutine.await() { callback in
Alamofire.request("blah blah blah", method: .post, parameters: parameters, encoding: .default).responseJSON { response in
let success = response["success"]
callback(success)
}
}
}
and then call this method inside coroutine:
DispatchQueue.main.startCoroutine {
let result = try AlamoTest()
}

Related

Dart Lang: Avoid marking all functions with async

Dart newbie here, I'm currently learning about asynchronous execution in Dart. I'm a bit irritated about how concurrency works in Dart, take the following scenario from their codelab:
void printOrderMessage () async {
try {
var order = await fetchUserOrder();
print('Awaiting user order...');
print(order);
} catch (err) {
print('Caught error: $err');
}
}
Future<String> fetchUserOrder() {
// Imagine that this function is more complex.
var str = Future.delayed(Duration(seconds: 4), () => throw 'Cannot locate user order');
return str;
}
Future<void> main() async {
await printOrderMessage();
}
In this case, the asynchronous operation is fetching the the user order from say a DB. Now because of Dart's await / async mechanism, every function that is related to the async operation is required to have a Future<> return type and must be tagged with async.
This feels clunky ... Imagine if some value deep in my function chain is being calculated asynchronously, would I really need to always return a future? Is there some other construct to synchronize code in Dart than await? Or have I misunderstood the concept?
If callers need to be able to wait for your asynchronous operation to finish, then your asynchronous function must return a Future that can be awaited. This is contagious; if callers of those callers need to be able to wait, then they too need to have Futures to wait upon.
If callers should not wait, then you can have a "fire-and-forget" function that does not need to return a Future:
Future<void> foo() {
// ...
}
// Does not need to return a Future. Consequently, callers cannot
// directly determine when `foo` completes.
void bar() {
foo();
}

Dart Future functions with callbacks on requests

I have an issue with understanding Future functions in dart and async programming. The examples I've found in web do not answer my question, so I would be grateful for community help.
I have a function that asks for players messages list:
Future getMessagesInfo(response) async{
dialogs = []; // I need this list to be filled to proceed
messagesList = response["data"];
await getDialogsFunc(messagesList);
renderMessages();
}
It is a callback for a previous request, which doesn't really matters here.
So, here is getDialogsFunc, which purpose is to runs through the list and ask playerInfo for each ID in the response:
Future getDialogsFunc(messagesList) async{
for(var i=0;i<messagesList.length;i++){
if(messagesList[i]["player2"] != player.ID) await rm.getDialogInfo(messagesList[i]["player2"]);
if(messagesList[i]["player1"] != player.ID) await rm.getDialogInfo(messagesList[i]["player1"]);
}
}
Here is getDialogInfo, which actually sends a request for playerInfo and has a callback function that handles received info:
Future getDialogInfo(int id) async{
messages = querySelector("account-messages-tab");
var request = new Request();
Object data = {"id": ServerAPI.PLAYER_INFO, "data":{"id": id}};
await request.sendRequest(data,false, messages.onGotDialogInfo);
}
The request is a simple class, that handles the requests:
class Request{
Future sendRequest(Object data, bool action,Function callback) async{
HttpRequest request = new HttpRequest();
String url = "http://example.com";
await request
..open('POST', url)
..onLoadEnd.listen((e)=> callback(JSON.decode(request.responseText)))
..send(JSON.encode(data));
}
}
And finally here is a callback for the request:
Future onGotDialogInfo(response) async{
List dialog = new List();
dialog.add(response["data"]["id"]);
dialog.add(response["data"]["login"]);
dialogs.add(dialog);
}
In the first function I wanted to run renderMessages() after I have received information about all messages, so that dialogs List should contain relevant information. In my realisation which I tested with breakpoints the renderMessages() functions runs BEFORE onGotDialogInfo callback.
What should I do to wait for the whole cycle getDialogsFunc functions and only then go to renderMessages()?
whenComplete is like finally, it's called no matter whether the request returned normally or with an error. Normally then is used.
getDialogsFunc uses async but doesn't use await which is a bit uncommon. This might be your intention, but I'm not sure
Future getDialogsFunc(messagesList) async {
for(var i=0;i<messagesList.length;i++){
if(messagesList[i]["player2"] != player.ID)
await rm.getDialogInfo(messagesList[i]["player2"]);
if(messagesList[i]["player1"] != player.ID)
await rm.getDialogInfo(messagesList[i]["player1"]);
}
}
getMessagesInfo could then look like:
void getMessagesInfo(response) await {
dialogs = []; // I need this list to be filled to proceed
messagesList = response["data"];
await getDialogsFunc(messagesList)
renderMessages(messagesList);
}
I don't know where Request comes from, therefore hard to tell how that code should look like. It should at least use await for other awaits to work as expected.
Future getDialogInfo(int id) async {
messages = querySelector("account-messages-tab");
var request = new Request();
Object data = {"id": ServerAPI.PLAYER_INFO, "data":{"id": id}};
final response = await request.sendRequest(data,false);
messages.onGotDialogInfo(response)
}
update
class Request{
Future sendRequest(Object data, bool action) async{
Completer completer = new Completer();
HttpRequest request = new HttpRequest();
String url = "http://example.com";
await request
..open('POST', url)
..onLoadEnd.listen((_) {
if (request.readyState == HttpRequest.DONE) {
if (request.status == 200) {
// data saved OK.
completer.complete(JSON.decode(request.responseText)); // output the response from the server
} else {
completer.completeError(request.status);
}
}
})
..send(JSON.encode(data));
return completer.future;
}
}

How to make simultaneous https requests in Swift 3

I'm having problems to execute a https requests, if the request don't have any error i never get the message, this is a command line tool application and i have a plist to allow http requests, i always see the completion block.
typealias escHandler = ( URLResponse?, Data? ) -> Void
func getRequest(url : URL, _ handler : #escaping escHandler){
let session = URLSession.shared
var request = URLRequest(url:url)
request.cachePolicy = .reloadIgnoringLocalCacheData
request.httpMethod = "GET"
let task = session.dataTask(with: url ){ (data,response,error) in
handler(response,data)
}
task.resume()
}
func startOp(action : #escaping () -> Void) -> BlockOperation{
let exOp = BlockOperation(block: action)
exOp.completionBlock = {
print("Finished")
}
return exOp
}
for sUrl in textFile.components(separatedBy: "\n"){
let url = URL(string: sUrl)!
let queu = startOp {
getRequest(url: url){ response, data in
print("REACHED")
}
}
operationQueue.addOperation(queu)
operationQueue.waitUntilAllOperationsAreFinished()
One problem is that your operation is merely starting the request, but because the request is performed asynchronously, the operation is immediately completing, not actually waiting for the request to finish. You don't want to complete the operation until the asynchronous request is done.
If you want to do this with operation queues, the trick is that you must subclass Operation and perform the necessary KVO for isExecuting and isFinished. You then change isExecuting when you start the request and isFinished when you finish the request, with the associated KVO for both. This is all outlined in the Concurrency Programming Guide: Defining a Custom Operation Object, notably in the Configuring Operations for Concurrent Execution section. (Note, this guide is a little outdated (it refers to the isConcurrent property, which has been replaced is isAsynchronous; it's focusing on Objective-C; etc.), but it introduces you to the issues.
Anyway, This is an abstract class that I use to encapsulate all of this asynchronous operation silliness:
/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `completeOperation()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
/// necessary and then ensuring that `completeOperation()` is called; or
/// override `cancel` method, calling `super.cancel()` and then cleaning-up
/// and ensuring `completeOperation()` is called.
public class AsynchronousOperation : Operation {
override public var isAsynchronous: Bool { return true }
private let lock = NSLock()
private var _executing: Bool = false
override private(set) public var isExecuting: Bool {
get {
return lock.synchronize { _executing }
}
set {
willChangeValue(forKey: "isExecuting")
lock.synchronize { _executing = newValue }
didChangeValue(forKey: "isExecuting")
}
}
private var _finished: Bool = false
override private(set) public var isFinished: Bool {
get {
return lock.synchronize { _finished }
}
set {
willChangeValue(forKey: "isFinished")
lock.synchronize { _finished = newValue }
didChangeValue(forKey: "isFinished")
}
}
/// Complete the operation
///
/// This will result in the appropriate KVN of isFinished and isExecuting
public func completeOperation() {
if isExecuting {
isExecuting = false
isFinished = true
}
}
override public func start() {
if isCancelled {
isFinished = true
return
}
isExecuting = true
main()
}
}
And I use this Apple extension to NSLocking to make sure I synchronize the state changes in the above (theirs was an extension called withCriticalSection on NSLock, but this is a slightly more generalized rendition, working on anything that conforms to NSLocking and handles closures that throw errors):
extension NSLocking {
/// Perform closure within lock.
///
/// An extension to `NSLocking` to simplify executing critical code.
///
/// - parameter block: The closure to be performed.
func synchronize<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}
Then, I can create a NetworkOperation which uses that:
class NetworkOperation: AsynchronousOperation {
var task: URLSessionTask!
init(session: URLSession, url: URL, requestCompletionHandler: #escaping (Data?, URLResponse?, Error?) -> ()) {
super.init()
task = session.dataTask(with: url) { data, response, error in
requestCompletionHandler(data, response, error)
self.completeOperation()
}
}
override func main() {
task.resume()
}
override func cancel() {
task.cancel()
super.cancel()
}
}
Anyway, having done that, I can now create operations for network requests, e.g.:
let queue = OperationQueue()
queue.name = "com.domain.app.network"
let url = URL(string: "http://...")!
let operation = NetworkOperation(session: .shared, url: url) { data, response, error in
guard let data = data, error == nil else {
print("\(error)")
return
}
let string = String(data: data, encoding: .utf8)
print("\(string)")
// do something with `data` here
}
let operation2 = BlockOperation {
print("done")
}
operation2.addDependency(operation)
queue.addOperations([operation, operation2], waitUntilFinished: false) // if you're using command line app, you'd might use `true` for `waitUntilFinished`, but with standard Cocoa apps, you generally would not
Note, in the above example, I added a second operation that just printed something, making it dependent on the first operation, to illustrate that the first operation isn't completed until the network request is done.
Obviously, you would generally never use the waitUntilAllOperationsAreFinished of your original example, nor the waitUntilFinished option of addOperations in my example. But because you're dealing with a command line app that you don't want to exit until these requests are done, this pattern is acceptable. (I only mention this for the sake of future readers who are surprised by the free-wheeling use of waitUntilFinished, which is generally inadvisable.)

How to make simultaneous async HTTP requests in swift 3, without AlamoFire

I wanna make a web crawling, currently i am reading a txt file with 12000 urls, i wanna use concurrency in this process, but the requests don't work.
typealias escHandler = ( URLResponse?, Data? ) -> Void
func getRequest(url : URL, _ handler : #escaping escHandler){
let session = URLSession(
configuration: .default,
delegate: nil,
delegateQueue: nil)
var request = URLRequest(url:url)
request.httpMethod = "GET"
let task = session.dataTask(with: request){ (data,response,error) in
handler(response,data)
}
task.resume()
}
for sUrl in textFile.components(separatedBy: "\n"){
let url = URL(string: sUrl)!
getRequest(url: url){ response,data in
print("RESPONSE REACHED")
}
}
If you have your URLSessions working correctly, all you need to go is create separate OperationQueue create a Operation for each of your async tasks you want completed, add it to your operation queue, and set your OperationQueue's maxConcurrentOperationCount to control how many of your tasks can run at one time. Puesdo code:
let operationQueue = OperationQueue()
operationQueue.qualityOfService = .utility
let exOperation = BlockOperation(block: {
//Your URLSessions go here.
})
exOperation.completionBlock = {
// A completionBlock if needed
}
operationQueue.addOperation(exOperation)
exOperation.start()
Using a OperationQueue subclass and Operation subclass will give you additional utilities for dealing with multiple threads.

Meteor method return inside mongo insert callback [duplicate]

Here is my server side code
Meteor.methods({
addSupportRequest: function (support) {
Support.insert(support, function (err, id) {
if (err)
throw new Meteor.Error(404, "Oops! Network Error. Please submit help request again. ");
console.log("Support resuest added: " + id);
return id;
});
} // End addSupportRequest
});
Here is the client side code
App.Util = {
call: function (method, params, callback) {
NProgress.start();
Meteor.apply(method, params, function (error, result) {
NProgress.done();
console.log(error);
console.log(result);
callback(error, result);
});
} // end Call
};
Please help me find out why meteor method call is not returning error or result. console.log() showing new record Id on server but showing undefined on client.
One issue that is preventing your method from returning a result is that return id; is in the function scope of the insert callback, and not the scope of the meteor method. So it will return from the callback and then there is no return in the meteor method function which is implicitly a return undefined.
You should add a return to the method's scope like this:
Meteor.methods({
addSupportRequest: function (support) {
return Support.insert(support, function (err, id) {
if (err)
throw new Meteor.Error(404, "Oops! Network Error. Please submit help request again. ");
console.log("Support resuest added: " + id);
return id;
});
} // End addSupportRequest
As for the error, I am not sure why it isn't surfacing as it should traverse up the call stack (doesn't matter that it is inside an inner function like the return) and since it is a Meteor.Error it should get sent to the client as well.
Dsyko's answer was somewhat on the right track. However, the asynchronous callback will never pass its result to the scope of the original function, which has ended.
What you want is to run the Support.insert operation synchronously, i.e. having the current fiber yield while I/O is happening. This is what the Meteor._wrapAsync function is for. Luckily for you, there is no need to do this manually because if you just take out the callback from the insert operation, it will run synchronously:
Meteor.methods({
addSupportRequest: function (support) {
var id = Support.insert(support);
// An error may be thrown here, but it certainly won't be a network error because the request will have already reached the server.
console.log("Support request added: " + id);
return id;
});
}

Resources