发出循环的HTTP请求

如何解决发出循环的HTTP请求

我正在编写与在线翻译API连接的翻译器应用程序。由于API的大小限制,我编写了程序来一次发送一个句子的文本,然后将翻译内容合并在一起。我已经循环播放

let serialQueue = DispatchQueue(label: "translationQueue")
        
        for line in lines {
          serialQueue.async {
            print("line is: " + line)
            
            var jpText = String(line)
            
            if jpText.isEmpty {
              jpText = "\n"
            }
            
            let escapedStr = jpText.addingPercentEncoding(withAllowedCharacters: (NSCharacterSet.urlQueryAllowed))
            let urlStr:String = ("https://api.mymemory.translated.net/get?q="+escapedStr!+"&langpair="+langStr!)
            let url = URL(string: urlStr)
            
            // Creating Http Request
            let request = NSURLRequest(url: url!)
            
            
            // If empty,don't feed to translator.
            if escapedStr!.isEmpty {
              //translatedLines.append("\n")
              self.enTextView.text = translatedLines
            }
            
            else {
              let configuration = URLSessionConfiguration.default
              configuration.waitsForConnectivity = true
              let defaultSession = URLSession(configuration: configuration,delegate: self,delegateQueue: nil)
              var dataTask: URLSessionDataTask?
              
              let group = DispatchGroup()
              group.enter()
              
              dataTask?.cancel()
              
              dataTask = defaultSession.dataTask(with: request as URLRequest) { [weak self] data,response,error in
                
                if let error = error {
                  // self?.errorMessage += "DataTask error: " + error.localizedDescription + "\n"
                  print("DataTask error: " + error.localizedDescription + "\n")
                  
                } else if
                  let data = data,let response = response as? HTTPURLResponse,response.statusCode == 200 {
                  let jsonDict: NSDictionary!=((try! JSONSerialization.jsonObject(with: data,options: JSONSerialization.ReadingOptions.mutableContainers)) as! NSDictionary)
                  
                  // if(jsonDict.value(forKey: "responseStatus") as! NSNumber == 200){
                  let responseData: NSDictionary = jsonDict.object(forKey: "responseData") as! NSDictionary
                  
                  group.leave()
                  
                  var translatedString = String()
                  translatedString = responseData.object(forKey: "translatedText") as! String
                  
                  let data = translatedString.data(using: .utf8)
                  
                  let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
                    .documentType: NSAttributedString.DocumentType.html,.characterEncoding: String.Encoding.utf8.rawValue
                  ]
                  
                  guard let attributedString = try? NSAttributedString(data: data!,options: options,documentAttributes: .none) else {
                    return
                  }
                  
                  let decodedString = attributedString.string
                  print("decoded: " + decodedString)
                  
                  translatedLines.append(decodedString)
                  translatedLines.append("\n")
                  
                  
                  DispatchQueue.main.async {
                    self?.enTextView.text = translatedLines
                  }
                }
              }
              
              dataTask?.resume()
              group.wait()
            }
          }
        }

但是翻译输出是随机排列的。我大致了解,正在发送并发请求。但是我可以在for循环中做什么以确保整个发送/接收都发生在移至下一个迭代之前?

解决方法

我将使用以下技术:

  1. 为翻译结果预分配一个数组。结果可能是成功(带有关联的翻译文本)或失败(带有关联的错误类型)。 Swift的Result类型在此用例中非常方便。
  2. 为每个数据任务分配一个索引(实际上是在步骤1中创建的数组中的索引)。
  3. 同时执行所有数据任务,并使用DispatchGroup进行协调。
  4. 成功完成或失败完成后,每个任务会将其结果写入数组。
  5. 所有数据任务完成后,调度组将通知主队列并执行我们的完成块,在此我们可以更新UI。

下面是一个根据您的大致松散的快速操场代码,该代码使用了我上面描述的技术:

let lines = ["one","two","three","four"]

enum TranslationError: Error {
    case neverRequested
    case countNotBuildUrl
    case dataTaskError(localizedDescription: String)
    case responseDidNotContainData
    case parsingError
}

let dispatchGroup = DispatchGroup()
var translationResults = [Result<String,TranslationError>](repeating: .failure(.neverRequested),count: lines.count)
for i in 0..<lines.count {
    dispatchGroup.enter()
    let line = lines[i]
    var urlComponents = URLComponents(string: "https://api.mymemory.translated.net/get")!
    urlComponents.queryItems = [
        URLQueryItem(name: "q",value: line),URLQueryItem(name: "langpair",value: "en|fr")
    ]
    guard let url = urlComponents.url else {
        translationResults[i] = .failure(.countNotBuildUrl)
        dispatchGroup.leave()
        continue
    }

    let dataTask = URLSession.shared.dataTask(with: url) { data,_,error in
        print("Data task #\(i) finished.")
        defer {
            dispatchGroup.leave()
        }
        if let error = error {
            DispatchQueue.main.async {
                translationResults[i] = .failure(.dataTaskError(localizedDescription: error.localizedDescription))
            }
            return
        }
        guard let data = data else {
            DispatchQueue.main.async {
                translationResults[i] = .failure(.responseDidNotContainData)
            }
            return
        }
        guard let jsonDict = try? JSONSerialization.jsonObject(with: data,options: []) as? NSDictionary,let responseData = jsonDict.value(forKey: "responseData") as? NSDictionary,let translatedLine = responseData.value(forKey: "translatedText") as? String else
        {
            DispatchQueue.main.async {
                translationResults[i] = .failure(.parsingError)
            }
            return
        }
        DispatchQueue.main.async {
            translationResults[i] = .success(translatedLine)
        }
    }
    print("Starting data task #\(i)...")
    dataTask.resume()
}
dispatchGroup.notify(queue: .main,execute: {
    // Handle errors and update the UI
    print(translationResults)
})

一些注意事项:

  1. 所有数据任务完成块更新同一队列上的translationResults数组很重要,这就是为什么我每次都要调用DispatchQueue.main.async的原因。不这样做可能会导致奇怪的崩溃,因为数组在Swift中不是线程安全的。
  2. 该技术不允许随着翻译的进行逐行增量更新UI,但是可以添加类似的内容。
  3. 在真实的应用程序中,我将重构此代码并将其拆分为职责更集中的几种方法。但是,为了更容易理解这个想法,我决定不这样做。
  4. 代码中的错误处理也是非常基本的,仅用于显示想法并保持其显示状态,并且不包括我在实际应用中所做的其他检查。
  5. 我很确定有更好和更优雅的方法来实现相同的结果,因此我很期待看到有人提出更好的实施方案。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?