Playwright-Go多API响应等待遇异常,请求代码排查
Playwright-Go多响应等待问题排查与修复
核心问题分析
你的代码出现日志2仅打印一次、日志4无法执行的情况,核心原因有以下几点:
WaitForResponse匹配阻塞:当传入字符串作为匹配条件时,Playwright会精确匹配响应的完整URL。如果实际请求存在重定向、URL编码差异(如参数顺序、特殊字符转义),会导致多数协程的WaitForResponse一直阻塞,无法执行后续代码和wg.Done(),最终wg.Wait()永远等待,日志4无法输出。defer wg.Done()位置错误:当前代码将defer放在函数末尾,若WaitForResponse一直阻塞,defer永远不会执行;即使等待出错触发panic,也无法提前保证WaitGroup计数递减。- 未处理等待错误:
page.WaitForResponse()返回错误值,忽略错误会导致等待失败时无法感知,协程直接异常退出,进一步导致WaitGroup计数无法正常递减。 - 固定数组存在越界风险:
[5]playwright.Response是固定长度数组,若dashboardIds长度超过5,赋值时会触发panic。
修复后的代码
func waitResponse(dashboardIds []int, page playwright.Page) { // 用切片替代固定数组,适配任意长度的dashboardIds responses := make([]playwright.Response, len(dashboardIds)) wg := sync.WaitGroup{} baseUrl := "https://example.com" for index, v := range dashboardIds { wg.Add(1) targetUrl := fmt.Sprintf("%s/%d?dashboard_id=114", baseUrl, v) currentIdx := index // 启动协程时传入局部变量,避免闭包变量捕获问题 go func(url string, idx int) { // 立即defer,确保协程无论正常退出还是异常退出,都能通知WaitGroup defer wg.Done() log.Printf("start request %s\n", url) // 1 // 使用函数式匹配,灵活性更高,可根据实际情况调整匹配逻辑 response, err := page.WaitForResponse(func(res playwright.Response) bool { // 示例:精确匹配URL,可替换为strings.Contains(res.URL(), url)实现包含匹配 return res.URL() == url }) if err != nil { log.Printf("wait for response %s failed: %v", url, err) return } log.Printf("goroutine get response from %s, index is %d\n", url, idx) // 2 resBody, err := response.Body() if err != nil { log.Printf("read response body for %s failed: %v", url, err) return } responses[idx] = response }(targetUrl, currentIdx) // 可选:若不需要全局监听响应,建议移除该注册,避免重复回调 page.On("response", func(res playwright.Response) { if res.URL() == targetUrl { log.Printf("listen get response from %s", targetUrl) //3 } }) } wg.Wait() log.Printf("all url responded") // 4 }
关键优化说明
- 灵活的URL匹配:使用函数式替代字符串匹配,可根据实际场景调整(如包含匹配、正则匹配),避免精确匹配的局限性。
- 可靠的
WaitGroup管理:在协程启动后立即defer wg.Done(),确保无论协程如何终止,都能正确递减计数。 - 错误处理:添加
WaitForResponse和response.Body()的错误处理,便于排查问题。 - 安全的存储结构:用切片替代固定长度数组,避免越界panic。
内容的提问来源于stack exchange,提问作者miracle
相关产品推荐
相关产品推荐

