使用HTMLKit时,如何让异步HTML页面加载任务串行执行?
解决异步任务串行执行的几种方案
你的问题核心是要让异步任务串行执行——前一个页面加载完成后,再启动下一个任务,避免循环瞬间触发所有异步调用导致的覆盖问题。下面是三种实用的实现方式:
方案一:用Async/Await(iOS 15+ / macOS 12+ 推荐)
先把原有的getPage改造成支持异步等待的版本,利用Swift原生并发特性:
func getPage(_ url: URL) async throws -> String? { // 先加载目标URL到WebView try await webView.loadURL(url) // 页面加载完成后执行JS获取内容 let result = try await webView.evaluateJavaScript("document.body.innerHTML") return result as? String }
然后在异步函数里顺序等待每个任务完成:
func processLinksSequentially(_ links: [URL]) async { for link in links { do { if let pageContent = try await getPage(link) { // 处理当前页面内容 print("完成处理:\(link)") } } catch { print("加载失败:\(link),错误:\(error)") } } } // 调用示例(比如在ViewController的viewDidLoad中) Task { await processLinksSequentially(links) }
注:如果webView.loadURL本身不支持Async/Await,可以用withCheckedThrowingContinuation封装回调版的加载方法,转成异步函数。
方案二:递归调用(兼容低版本系统)
如果项目需要支持iOS 15以下,递归是最直接的手动串行控制方式:
func processNextLink(from links: [URL], index: Int = 0) { guard index < links.count else { // 所有链接处理完毕 return } let currentLink = links[index] // 先加载当前链接到WebView webView.load(URLRequest(url: currentLink)) // 实际开发中必须依赖WKNavigationDelegate的didFinishNavigation确认页面加载完成,再执行JS webView.evaluateJavaScript("document.body.innerHTML") { [weak self] result, error in guard let self = self else { return } if let error = error { print("加载失败:\(currentLink),错误:\(error)") } else if let content = result as? String { // 处理当前页面内容 print("完成处理:\(currentLink)") } // 递归触发下一个链接的加载 self.processNextLink(from: links, index: index + 1) } } // 调用示例 processNextLink(from: links)
方案三:用OperationQueue串行队列
创建一个串行队列,限制并发数为1,确保任务按顺序执行:
let serialQueue = OperationQueue() serialQueue.maxConcurrentOperationCount = 1 // 强制串行执行 func processLinksWithQueue(_ links: [URL]) { for link in links { serialQueue.addOperation { [weak self] in guard let self = self else { return } // 用信号量将异步操作转为同步等待 let semaphore = DispatchSemaphore(value: 0) DispatchQueue.main.async { self.webView.load(URLRequest(url: link)) self.webView.evaluateJavaScript("document.body.innerHTML") { result, error in // 处理加载结果 if let error = error { print("加载失败:\(link),错误:\(error)") } else if let content = result as? String { print("完成处理:\(link)") } semaphore.signal() // 任务完成,释放信号量 } } semaphore.wait() // 等待当前任务完成,再执行下一个 } } } // 调用示例 processLinksWithQueue(links)
内容的提问来源于stack exchange,提问作者Duck
相关产品推荐
相关产品推荐

