并发控制
并发控制用于管理多个请求同时发送时的行为。Keq 提供了两种控制策略:串行执行(serial)和中止旧请求(abort)。
为什么需要并发控制?
- 搜索框输入:用户快速输入时会触发多个搜索请求,但我们只关心最新的搜索结果
- 批量请求:处理大量请求时,受限于浏览器的并发连接数,需要控制并发量
- 防止重复提交:用户连续点击按钮时,避免发送多个相同的请求
串行执行 - serial
串行模式确保同一个队列中的请求按顺序执行,当上一个请求未完成时,后续请求会等待。
基本用法
import { request } from "keq"
await request
.get("/api/cats")
.flowControl("serial"/*, context.locationId */)
await request
.get("/api/cats/random")
.flowControl("serial", "cat-api") // 命名队列实际场景:批量请求
处理多个请求时,确保它们按顺序执行:
import { request } from "keq";
const catIds = [1, 2, 3, 4, 5];
// 所有请求会串行执行,而不是并发
await Promise.all(
catIds.map((id) =>
request
.get(`/api/cats/${id}`)
.flowControl("serial", "cat-fetch")
)
);性能提示
浏览器对同一域名的并发 HTTP 连接数有限制(通常为 6 个)。使用 serial 模式可以精确控制并发数,避免请求排队。
中止旧请求 - abort
abort 模式在发送新请求时会自动中止同一队列中未完成的旧请求,确保只有最新的请求在执行。
基本用法
import { request } from "keq"
await request
.get("/api/cats/search")
.flowControl("abort"/*, context.locationId */)
await request
.get("/api/cats/search")
.flowControl("abort", "cat-search"); // 命名队列实际场景:搜索建议
用户快速输入时,只展示最新的搜索结果:
import { request, AbortException } from "keq"
import { useState } from "react"
function SearchBox() {
const [suggestions, setSuggestions] = useState<string[]>([])
const handleSearch = async (keyword: string) => {
try {
const results = await request
.get("/api/cats/search")
.query("q", keyword)
.flowControl("abort", "cat-search")
setSuggestions(results)
} catch (err) {
// 旧请求被中止时会抛出 AbortException
if (!(err instanceof AbortException)) {
console.error(err)
}
}
}
return (
<div>
<input
type="text"
onChange={(e) => handleSearch(e.target.value)}
placeholder="搜索中..."
/>
<ul>
{suggestions.map((suggestion, i) => (
<li key={i}>{suggestion}</li>
))
</ul>
</div>
)
}错误处理
当请求被中止时,会抛出一个 AbortException。在实际应用中,通常需要捕获并忽略这个错误。
队列隔离
不同的命名队列之间完全独立,互不影响:
import { request } from "keq";
// 这两个请求在不同的队列中,可以并发执行
await Promise.all([
request.get("/api/cats").flowControl("serial", "cats"),
request.get("/api/dogs").flowControl("serial", "dogs"),
]);实例隔离
不同的 KeqRequest 实例之间的并发控制队列是完全隔离的:
import { KeqRequest } from "keq";
const api1 = new KeqRequest();
const api2 = new KeqRequest();
// 即使使用相同的 key,两个实例的队列也是独立的
await Promise.all([
api1.get("/api/cats").flowControl("serial", "my-key"),
api2.get("/api/cats").flowControl("serial", "my-key"),
]);