generator 本身不直接控制网络请求的暂停与恢复,而是通过结构化任务序列、按需交出控制权、配合 promise 状态驱动执行节奏来实现可控队列调度;它封装待爬 url 队列为可迭代“动作流”,每个 yield 返回含元信息的任务对象,分离任务生成与执行,支持节流、重试、暂停/恢复及外部中断协同。

Generator 本身不直接控制网络请求的暂停与恢复,但它能天然配合异步流程管理,成为实现可控队列调度的理想协程载体。关键不在 Generator “暂停网络”,而在于用它结构化任务序列、按需交出控制权、配合 Promise 状态驱动执行节奏。
用 Generator 封装待爬 URL 队列
把爬取任务抽象为一个可迭代的“动作流”,每个 yield 返回一个带元信息的对象(如 URL、重试次数、延迟毫秒),而非直接发起请求:
function* crawlQueue(urls) {
for (const url of urls) {
yield {
url,
delay: 1000, // 下一条前等待
maxRetries: 3,
priority: 'high'
};
}
}这样 Generator 只负责“产出任务描述”,不耦合 HTTP 调用逻辑,便于统一拦截、节流、重试或中途终止。
配合 async/await 手动驱动执行流
写一个控制器函数,用 next() 拿到下一个任务,再用 await fetch() 执行,并根据返回结果决定是否继续、跳过或暂停:
- 调用
iterator.next()获取任务对象 - 若
done === true,队列结束 - 否则
await fetchWithRetry(task),成功则continue;失败且可重试则iterator.next()重入当前任务(需在 Generator 内部支持重试标记) - 遇到
task.delay,用await new Promise(r => setTimeout(r, task.delay))实现可控等待
实现外部暂停/恢复信号机制
Generator 无法被外部中断,但可通过共享状态变量 + yield 检查点来模拟暂停:
let isPaused = false;
let resumeResolve;
<p>function* crawlWithPause(urls) {
for (const url of urls) {
while (isPaused) {
yield { type: 'PAUSED', url }; // 告知外部已暂停
await new Promise(r => resumeResolve = r);
}
yield { url, status: 'READY' };
}
}</p><p>// 外部控制
function pause() { isPaused = true; }
function resume() {
isPaused = false;
if (resumeResolve) resumeResolve();
}</p>每次循环前检查 isPaused,暂停时 yield 并等待 resumeResolve 被调用 —— 这就实现了用户可干预的“软暂停”。
集成到真实爬虫中的实用建议
- 不要让 Generator 直接
fetch,始终分离“任务生成”和“任务执行”两层 - 用
Symbol.iterator或自定义next()方法包装 Generator,方便注入中间件(如限速、UA 轮换、代理切换) - 结合
AbortController控制单个 fetch 请求超时或取消,与 Generator 的暂停解耦但协同使用 - 错误处理放在执行层,Generator 层只负责重试策略的声明(例如 yield { url, retry: 2 }),由控制器解读
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











