Alamofire串行请求

3

我希望请求按顺序执行,但使用Alamofire时无法实现。

我想按顺序打印1到30(假设响应只是参数的回显)。

// Only 1 connection per Host

let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPMaximumConnectionsPerHost =  1
configuration.timeoutIntervalForRequest = 30
self.manager = Alamofire.Manager(configuration: configuration) 

for i in 1...30 {
    manager.request(.GET, "http://httpbin.org/get", "i" : i], encoding: .JSON)
        .responseJSON { response in
            switch (response.result){
            case .Failure(let error):
                print("error")
                break;
            case .Success(let json):
                print(json)
            }
     })

1
你表达了你的期望,但没有说明你的结果。如果你没有看到1...30,那么你看到了什么,你认为这意味着什么是错误的? - Jonah
@Jonah,我看到了1到30的数字,但是它们没有按顺序排列。 - Guilherme Torres Castro
3个回答

8
根据NSURLSessionConfiguration文档:
此属性确定基于此配置的会话中任务对每个主机同时进行的最大连接数。此限制是每个会话的,因此如果您使用多个会话,则整个应用程序可能超出此限制。另外,根据您与互联网的连接,会话可能使用比您指定的低限制更低的限制。在OS X中,默认值为6,而在iOS中为4。
正如您所看到的,此设置仅在网络级别上控制连接数。一旦使用NSURLSession排队了若干请求,它就取决于该类确定何时进行请求,这是Alamofire的底层。没有方法可以使用NSURLSession或Alamofire保证请求的顺序,除非明确编写它们。
话虽如此,通过将请求包装在NSOperation中,您可以获得所需的行为。如果创建具有1的.maxConcurrentOperationCount的NSOperationQueue,则可以创建串行队列。然后,使用您已经编写的相同循环,您应该能够像这样包装您的Alamofire请求:
queue.addOperationWithBlock {
    manager.request(.GET, "http://httpbin.org/get", "i" : i], encoding: .JSON)
        .responseJSON { response in
            switch (response.result){
            case .Failure(let error):
                print("error")
                break;
            case .Success(let json):
                print(json)
            }
     })
}
 

使用.maxConcurrentOperationCount属性设置为1,队列应该按顺序执行,正如我之前提到的一样。因此,您的操作将按照它们添加到队列中的顺序执行,这是根据NSOperationQueue的文档说明的。所以您应该能够看到您想要的1到30的结果。
所有这些说法,除非这只是一个编程练习来按顺序获得这些结果,否则解决您想解决的问题可能有更好的解决方案。

谢谢!是的,我很困惑为什么请求没有按顺序执行。 - Guilherme Torres Castro
这不对,manager.request是异步的并立即返回,像那样包装它实际上什么也没做。 - Peter Lapisu
只要将 maxConcurrentOperationCount 设置为 1,它就可以正常工作。 - Jon Shier

1
Alamofire 5:

import UIKit
import Alamofire

class ViewController: UIViewController {

    var session: Session?
    var opQueue: OperationQueue?
    var semaphore: DispatchSemaphore?
    
    override func viewDidLoad() {
        super.viewDidLoad()

        synchronous()
    }

    
    func synchronous() {
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 30
        
        self.session = Session(configuration: configuration)
        self.opQueue = OperationQueue()
        self.opQueue?.maxConcurrentOperationCount = 1
        self.semaphore = DispatchSemaphore(value: 0)

        for i in 1...30 {
            self.opQueue?.addOperation { [weak self] in
                self?.session?.request("https://httpbin.org/get", method: .get, parameters: ["i" : i]).responseJSON(completionHandler: { response  in
                        switch (response.result){
                        case .failure(let error):
                            print("error: \(error)")
                            break;
                        case .success(let json):
                            if let json = json as? Dictionary<String, Any>, let data = json["args"] as? Dictionary<String, Any> {
                                print(data)
                            }
                            
                        }
                    self?.semaphore?.signal()
                 })
                
                self?.semaphore?.wait()
            }
            
        }
    }

}


日志:

["i": 1]
["i": 2]
["i": 3]
["i": 4]
["i": 5]
["i": 6]
["i": 7]
["i": 8]
["i": 9]
["i": 10]
["i": 11]
["i": 12]
["i": 13]
["i": 14]
["i": 15]
["i": 16]
["i": 17]
["i": 18]
["i": 19]
["i": 20]
["i": 21]
["i": 22]
["i": 23]
["i": 24]
["i": 25]
["i": 26]
["i": 27]
["i": 28]
["i": 29]
["i": 30]

1

Alamofire的Github页面所述:

在Alamofire中进行网络操作是异步完成的。对于不熟悉这个概念的程序员来说,异步编程可能是一种沮丧的来源,但采用这种方式有很好的理由

因此,使用Alamofire本质上就是在进行异步网络调用。您可以选择其他库或使用基础SDK实现,但正如从Alamofire的Github页面链接的苹果文档中详细说明的那样,几乎所有网络库都是异步的。

因此,为您解释一下,Alamofire的接口会同步接收您的调用,但在此之后,不能保证响应返回的顺序。它们可能按不同的顺序发出请求,并且几乎肯定会以不同的顺序返回响应。

更好的选择是存储一个可变数组的响应,一旦从您进行的每个网络调用中存储了一个响应,则对数组进行排序,然后进行打印操作。


1
是的,我知道Alamofire是异步的,以及异步编程的所有好处。应用程序中的所有其他请求都是这样的。事实上,我已经找到了解决我的问题的方法。真正的问题在于:“为什么HTTPMaximumConnectionsPerHost = 1不像预期的那样工作?”或者HTTPMaximumConnectionsPerHost属性的真正含义是什么?如果问题不太清楚,对不起。 - Guilherme Torres Castro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接