提问人:Martin Claesson 提问时间:7/20/2023 最后编辑:HangarRashMartin Claesson 更新时间:7/21/2023 访问量:44
如何使用完成处理程序按顺序执行函数?
How to execute function with completion handler sequentially?
问:
我有一个 Swift 函数,它使用 JavaScript 注入根据给定的 CSS 选择器填充 WKWebView 中的表单字段。JS 函数返回已归档字段的数量。我想修改 Swift 函数,以便它按顺序尝试每个选择器,如果其中一个选择器成功填充了一个字段(或多个字段),该函数应该提前返回,而无需尝试其余的选择器。fillField
例如,函数由以下方式触发:
fillField(in: webView, selectors: ["input[autocomplete=\"username\"]", "input[name=\"username\" i]"], value: "test_value") { fills in
console.debug("Auto-filled \(fills) fields")
}
下面是 fillField 函数的现有代码:
private func fillField(in webView: WKWebView, selectors: [String], value: String? = nil, checked: Bool? = nil, completion: @escaping (Int) -> Void) {
for selector in selectors {
let script: String
if let value = value {
script = String(format: "AutoFill.setValue(\'%@\', \'%@\', \'%@\')", selector, "value", value)
} else if let checked = checked {
script = String(format: "AutoFill.setValue(\'%@\', \'%@\', \'%@\')", selector, "checked", checked ? "true": "false")
} else {
console.warning("No value or checked provided")
completion(0)
return
}
console.info(script)
webView.evaluateJavaScript(script) { result, error in
if let result = result as? Int {
// Result = number of filed fields
if result > 0 {
// STOP EXECUTING
}
}
}
}
completion(0)
return
}
我想确保一旦选择器成功填充一个字段(或多个字段),该函数应停止处理并提前返回,从而避免对其余选择器进行不必要的评估。
我尝试使用但它不起作用,因为需要在主线程中执行。DispatchSemaphore
evaluateJavaScript
任何关于如何解决的想法都非常感谢。
答:
0赞
Claudio
7/21/2023
#1
尝试这种方法,想法是使用 DispatchGroup 并使循环按顺序运行,因为 JS 是一种单线程语言,它应该不是问题,并且像以前一样工作。代码不能在主线程上运行,因为 wait() 会阻止它,因此它应该在另一个线程上运行,等待在主线程上运行的代码完成。此方法使用 DispatchGroup,但 DispatchSemaphore 也应该有效。
private func fillField(in webView: WKWebView, selectors: [String], value: String? = nil, checked: Bool? = nil, completion: @escaping (Int) -> Void) {
DispatchQueue.global(qos: .userInitiated).async {
let dispatchGroup = DispatchGroup()
for selector in selectors {
let script: String
if let value = value {
script = String(format: "AutoFill.setValue(\'%@\', \'%@\', \'%@\')", selector, "value", value)
} else if let checked = checked {
script = String(format: "AutoFill.setValue(\'%@\', \'%@\', \'%@\')", selector, "checked", checked ? "true": "false")
} else {
console.warning("No value or checked provided")
completion(0)
return
}
console.info(script)
dispatchGroup.enter()
var shouldStopLoop = false
webView.evaluateJavaScript(script) { result, error in
if let result = result as? Int {
// Result = number of filed fields
if result > 0 {
// STOP EXECUTING
shouldStopLoop = true
}
}
dispatchGroup.leave()
}
dispatchGroup.wait() // can set a timeout
if shouldStopLoop {
break // stop loop or call completion
}
}
// maybe switch dispatchqueue before calling this
completion(0)
}
}
评论