1

i am using a single serial queue as the rootQueue / request / serialization queue

let queue = DispatchQueue(label: "APIManager.rootQueue")
a.session = Session(configuration: sessionConfiguration,
                    delegate: a,
                    rootQueue: queue,
                    startRequestsImmediately: true,
                    requestQueue: queue,
                    serializationQueue: queue,
                    interceptor: nil,
                    serverTrustManager: nil,
                    redirectHandler: nil,
                    cachedResponseHandler: nil,
                    eventMonitors: [])

however the requests seems to be completed in random order as to the order they were created

Compleations print to console:

[2][Request]: GET https://...
[1][Request]: GET https://...
[4][Request]: GET https://...
[3][Request]: GET https://...
[5][Request]: GET https://...

the number in [] represent the creation order PS: the order is also wrong during serialization

the queue is serial but the results looks like from concurrent

how to maintain a FIFO order of requests and results

Peter Lapisu
  • 19,915
  • 16
  • 123
  • 179
  • 1
    Completion is underorder, while the "launch" is in sequence... Imagine your fifth request takes 5 seconds to complete, but the first one which is heavy takes 30 seconds, that seems to be pretty beavhior to me. If you want to only launch the second request after the first one is launched, listen to the completion of the first one before launching it. And so on. – Larme Nov 15 '21 at 13:26
  • i would expect to launch the next rocket as soon the previous did land, eg. serial behavior... it is also important so the server doesn't get stalled like when launching an app, where let's say several requests are performed and the server gets concurrently nuked – Peter Lapisu Nov 15 '21 at 13:48
  • Doc for requestQueue: `DispatchQueue on which to perform URLRequest creation. By default this queue will use the rootQueue as its target. A separate queue can be used if it's determined request creation is a bottleneck, but that should only be done after careful testing and profiling. nil by default.` It's only the creation that's serial in your case. They are launched when possible, but their is no guarantee for their completion. It's like a default `URLSession.shared.dataTask(...).resume()` after all, If you launch 10 of them, completion order is not guaranteed. – Larme Nov 15 '21 at 13:52
  • ok, than the question remains: "how to maintain a FIFO order of requests and results" – Peter Lapisu Nov 15 '21 at 13:55
  • I think @Larme's suggestion to just launch the second request when the completion for the first was called is the way to go. You could try chaining the requests with the new async/await from Swift 5.5, or do the good old way with simple completion handlers. – Starsky Nov 15 '21 at 15:23
  • but the requests may by triggered by different managers, using the same underlying apimanager... so you cannot chain them, but expect whatever is put in before them is executed accordingly – Peter Lapisu Nov 15 '21 at 15:36
  • I'm wondering if by using a custom the `RequestInterceptor`, you can delay here the call of next method if it didn't complete. You'd need to listen to each call, maybe using a custom `serializationQueue` for that, or another mecanism (overriding maybe all `completion()`). But is it called in case of errors, or only in success)? I'm thinking "out loud", I don't know if it really can be done that way... – Larme Nov 15 '21 at 15:40
  • A little more digging: `EventMonitor` seems to be a good way to listen to completion and then allow the next request to follow... – Larme Nov 15 '21 at 15:59
  • i was thinking about a custom operation queue, DispatchQueue subclass, but looks too brute... the EventMonitor could be a way too – Peter Lapisu Nov 15 '21 at 16:07
  • 1
    Else, wrapping Alamofire, and enqueing yourself the request could be a way too... – Larme Nov 15 '21 at 16:28
  • well the wrapping looks like the most feasible solution for now... the wrapper wraps the request and provides completion entry point... than startRequestsImmediately = false and launch the requests myself after the completion – Peter Lapisu Nov 16 '21 at 09:11

1 Answers1

1

If you want to make requests in sequence rather than in parallel you can:

  1. Nest them. Start the next request inside the completion handler of the previous request.
  2. Wrap Alamofire requests in Operations and set dependencies between the operations such that they execute in the order you want.
  3. Use Alamofire's Combine integration to publish the first request's response, then flatMap in the next request.
  4. Use Swift's new concurrency feature to wrap Alamofire's completion handlers so you can use the async-await syntax. This will be supported by Alamofire directly in an upcoming release.

Ultimately, you probably want to reevaluate whether FIFO is what you really want, or whether you can build a system that doesn't care when requests complete. Such a system will likely be easier to manage, as you remove dependencies between requests, and higher performance, as you remove any waiting necessary to ensure the requests are in the right order.

Jon Shier
  • 12,200
  • 3
  • 35
  • 37