惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
博客园 - 叶小钗
H
Help Net Security
P
Privacy & Cybersecurity Law Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Help Net Security
Help Net Security
Hugging Face - Blog
Hugging Face - Blog
D
Docker
Security Archives - TechRepublic
Security Archives - TechRepublic
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
V2EX - 技术
V2EX - 技术
人人都是产品经理
人人都是产品经理
L
LINUX DO - 最新话题
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Google Online Security Blog
Google Online Security Blog
博客园 - 聂微东
WordPress大学
WordPress大学
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Latest news
Latest news
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
B
Blog RSS Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Blog — PlanetScale
Blog — PlanetScale
C
Cisco Blogs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Know Your Adversary
Know Your Adversary
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
爱范儿
爱范儿
Webroot Blog
Webroot Blog
宝玉的分享
宝玉的分享
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
Recent Commits to openclaw:main
Recent Commits to openclaw:main
The Register - Security
The Register - Security
Simon Willison's Weblog
Simon Willison's Weblog
A
Arctic Wolf
Scott Helme
Scott Helme
The Last Watchdog
The Last Watchdog
Y
Y Combinator Blog
Last Week in AI
Last Week in AI
S
Securelist
Cloudbric
Cloudbric
G
GRAHAM CLULEY
M
MIT News - Artificial intelligence
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
Visual Studio Blog
S
Schneier on Security
Engineering at Meta
Engineering at Meta

博客园 - 悠哉大斌

AI Agent 安全沙箱技术对比:E2B、Cube Sandbox 与 Docker Sandboxes linux 升级 claude code的坑 debian(WSL) apt 代理配置 linux内核之Namespaces、Cgroups、Capabilities、Seccomp和Landlock 大语言模型推理中的隐藏瓶颈及其解决方法 Docker Sandboxes 技术是什么,和docker容器有什么区别 什么是 Docker Agent以及一个Docker Agent示例 windows wsl 安装 Temurin® JDK claude Code 和 codex 作为编程代理,前者使用typescript开发,后者使用rust,他们的决策依据是什么 Java 并发编程发展史与 java.util.concurrent 全景解析 LLM 真的改变了编码风格吗? 如果人工智能会犯错且不精确,为什么它正在改变世界? Python 的协程模型和 JavaScript 的 async/await python闭包和function.__closure__特殊属性 python 的 dunder name和 sunder name 基于nodejs设计REST API的知名开源框架 windows上使用node-oracledb Thick 模式连接 Oracle 11g Claude Code CLI连接 DeepSeek V4模型后调用报400错误 python里对象(object)到底是什么 Python 泛型演变史 Python 类型别名的演变 Python 类型提示的演变史 PEP 593 新增的Annotated 类型 Alibaba AgentScope 和 microsoft agent framework 详细对比分析 spring AI Alibaba Agent Framework 和 agentscope有什么区别和联系 AI Agent协作模式以及主流开源框架对协作模式的支持 Tailscale 是如何接管 DNS 的? Agent = Model + Harness 使用rust编写typescript编译器的难点在什么地方,哪些数据结构是rust不擅长的? TypeScript/JavaScript 中的异步迭代语句 Tailscale Serve and Funnel openclaw gateway的网络绑定模式 websocket协议和http协议有何依赖关系? MCP通信的双方是谁? claude code MCP 安装范围 如何在wsl2环境下给claude code cli 配置 playwright-mcp wsl的网络模式有哪几种,有哪些区别? AI Agent memory是什么? ai agent skills是什么? Go测试生态系统工具与最佳实践深度调研(聚焦认证授权系统) 线性代数中常见矩阵类型的概念关系思维导图 三种主流授权策略
js中的生成器函数
悠哉大斌 · 2026-02-06 · via 博客园 - 悠哉大斌

JavaScript 的生成器函数(Generator Function)是一种特殊的函数,它可以暂停执行恢复执行,并可以产出(yield)多个值(而普通函数只能返回单个值)。

基本概念

1. 创建生成器函数

function* generatorFunction() {
  yield '第一个值';
  yield '第二个值';
  yield '第三个值';
}

注意:使用 function* 声明(星号可以放在 function 后面或函数名前)

2. 基本使用

function* simpleGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = simpleGenerator();

console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }

主要特性

1. 暂停和恢复执行

function* counter() {
  console.log('开始');
  yield '暂停点1';
  console.log('继续执行');
  yield '暂停点2';
  return '结束';
}

const gen = counter();
console.log(gen.next()); // 开始 → { value: '暂停点1', done: false }
console.log(gen.next()); // 继续执行 → { value: '暂停点2', done: false }
console.log(gen.next()); // { value: '结束', done: true }

2. 双向通信

可以向生成器传递值:

function* twoWayCommunication() {
  const name = yield '你的名字是什么?';
  const age = yield `你好 ${name},你多大了?`;
  return `${name} 今年 ${age} 岁`;
}

const gen = twoWayCommunication();
console.log(gen.next());    // { value: '你的名字是什么?', done: false }
console.log(gen.next('小明')); // { value: '你好 小明,你多大了?', done: false }
console.log(gen.next(25));  // { value: '小明 今年 25 岁', done: true }

3. 无限序列生成

function* infiniteNumbers() {
  let n = 0;
  while (true) {
    yield n++;
  }
}

const numbers = infiniteNumbers();
console.log(numbers.next().value); // 0
console.log(numbers.next().value); // 1
console.log(numbers.next().value); // 2
// 可以无限调用...

实用场景

1. 惰性计算

function* take(n, generator) {
  for (let i = 0; i < n; i++) {
    yield generator.next().value;
  }
}

function* fibonacci() {
  let [prev, curr] = [0, 1];
  while (true) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

// 只获取前10个斐波那契数
const firstTen = [...take(10, fibonacci())];
console.log(firstTen); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

2. 异步操作控制(async/await 出现前)

function asyncTask(time) {
  return new Promise(resolve => setTimeout(() => resolve(`完成 ${time}ms`), time));
}

function* asyncGenerator() {
  const result1 = yield asyncTask(1000);
  console.log(result1);
  
  const result2 = yield asyncTask(500);
  console.log(result2);
}

// 手动执行生成器
function run(generator) {
  const gen = generator();
  
  function handle(result) {
    if (result.done) return Promise.resolve(result.value);
    return Promise.resolve(result.value)
      .then(data => handle(gen.next(data)))
      .catch(err => gen.throw(err));
  }
  
  return handle(gen.next());
}

run(asyncGenerator);

3. 状态机实现

function* stateMachine() {
  let state = '开始';
  
  while (true) {
    const action = yield state;
    
    if (action === '下一步' && state === '开始') {
      state = '进行中';
    } else if (action === '下一步' && state === '进行中') {
      state = '完成';
    } else if (action === '重置') {
      state = '开始';
    }
  }
}

const machine = stateMachine();
console.log(machine.next().value); // '开始'
console.log(machine.next('下一步').value); // '进行中'
console.log(machine.next('下一步').value); // '完成'
console.log(machine.next('重置').value); // '开始'

4. 可迭代对象

生成器函数返回的是可迭代对象:

function* range(start, end) {
  for (let i = start; i <= end; i++) {
    yield i;
  }
}

// 可以直接用于 for...of
for (let num of range(1, 5)) {
  console.log(num); // 1, 2, 3, 4, 5
}

// 或者使用扩展运算符
const numbers = [...range(1, 3)];
console.log(numbers); // [1, 2, 3]

方法总结

生成器对象的方法:

function* sample() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = sample();

// 1. next() - 获取下一个值
gen.next();

// 2. return() - 结束生成器
gen.return('提前结束');

// 3. throw() - 向生成器抛出错误
gen.throw(new Error('出错了'));

注意事项

  1. 箭头函数不能作为生成器(箭头函数没有 function 关键字)
  2. 生成器函数调用时不立即执行,而是返回一个生成器对象
  3. 生成器对象既是迭代器也是可迭代对象
  4. 在类中也可以使用生成器方法:
    class MyClass {
      *generatorMethod() {
        yield 'hello';
      }
    }
    

异步生成器函数(Async Generator Functions)

异步生成器函数是生成器函数和异步函数的结合体,可以异步地产生一系列值。

基本语法

async function* asyncGenerator() {
  yield await Promise.resolve(1);
  yield await Promise.resolve(2);
  yield await Promise.resolve(3);
}

或使用简写形式:

async function* asyncGenerator() {
  yield 1;  // 自动包装为 Promise
  yield 2;
  yield 3;
}

基本使用

1. 创建和使用

async function* fetchInSequence() {
  const data1 = await fetch('https://api.example.com/data1').then(r => r.json());
  yield data1;
  
  const data2 = await fetch('https://api.example.com/data2').then(r => r.json());
  yield data2;
  
  const data3 = await fetch('https://api.example.com/data3').then(r => r.json());
  yield data3;
}

2. 遍历异步生成器

async function processAsyncGenerator() {
  const gen = fetchInSequence();
  
  for await (const data of gen) {
    console.log(data);
    // 每个数据会在上一个数据完全获取后才会开始获取下一个
  }
}

主要特性

1. for await...of 循环

专门用于遍历异步迭代器:

async function* timer(seconds) {
  for (let i = 1; i <= seconds; i++) {
    await new Promise(resolve => setTimeout(resolve, 1000));
    yield `第 ${i} 秒`;
  }
}

async function run() {
  for await (const tick of timer(5)) {
    console.log(tick);
  }
  console.log('计时结束');
}
// 输出:第 1 秒 → 第 2 秒 → ... → 第 5 秒 → 计时结束

2. 返回值是 AsyncGenerator 对象

async function* example() {
  yield 1;
  yield 2;
  return 3;  // return 的值在 done: true 时返回
}

async function run() {
  const gen = example();
  
  console.log(await gen.next()); // { value: 1, done: false }
  console.log(await gen.next()); // { value: 2, done: false }
  console.log(await gen.next()); // { value: 3, done: true }
}

实用场景

1. 分页数据获取

async function* paginatedData(url) {
  let nextUrl = url;
  
  while (nextUrl) {
    const response = await fetch(nextUrl);
    const data = await response.json();
    
    yield data.results;  // 假设 API 返回 { results: [...], next: 'url' }
    
    nextUrl = data.next; // 下一页的 URL
  }
}

// 使用
async function getAllData() {
  const allData = [];
  
  for await (const page of paginatedData('https://api.example.com/data?page=1')) {
    allData.push(...page);
    console.log(`已获取 ${allData.length} 条数据`);
  }
  
  return allData;
}

2. 实时数据流处理

async function* eventStream(eventSource) {
  const controller = new AbortController();
  const { signal } = controller;
  
  try {
    const response = await fetch(eventSource, { signal });
    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      
      const chunk = decoder.decode(value);
      const events = chunk.split('\n\n'); // SSE 格式
      
      for (const event of events) {
        if (event.trim()) {
          yield JSON.parse(event.match(/{.*}/)?.[0] || '{}');
        }
      }
    }
  } finally {
    controller.abort();
  }
}

// 使用 SSE(Server-Sent Events)
async function handleRealTimeData() {
  for await (const event of eventStream('/api/events')) {
    updateUI(event);
  }
}

3. 批量处理大型数据集

async function* batchProcessor(dataSource, batchSize = 100) {
  let offset = 0;
  let hasMore = true;
  
  while (hasMore) {
    const batch = await fetchBatch(dataSource, offset, batchSize);
    
    if (batch.length === 0) {
      hasMore = false;
    } else {
      // 处理当前批次
      const processed = await processBatchAsync(batch);
      yield processed;
      
      offset += batchSize;
      
      // 模拟延迟,避免服务器过载
      await new Promise(resolve => setTimeout(resolve, 100));
    }
  }
}

async function fetchBatch(source, offset, limit) {
  // 实际的数据获取逻辑
  return fetch(`${source}?offset=${offset}&limit=${limit}`)
    .then(r => r.json());
}

4. 并发控制的任务队列

async function* taskRunner(tasks, concurrency = 3) {
  const executing = new Set();
  
  for (const task of tasks) {
    const promise = task().then(result => {
      executing.delete(promise);
      return result;
    });
    
    executing.add(promise);
    
    if (executing.size >= concurrency) {
      // 等待至少一个任务完成
      yield Promise.race(executing);
    }
  }
  
  // 等待所有剩余任务完成
  while (executing.size > 0) {
    yield Promise.race(executing);
  }
}

// 使用
async function runConcurrentTasks() {
  const tasks = Array.from({ length: 10 }, (_, i) => 
    () => new Promise(resolve => 
      setTimeout(() => resolve(`任务 ${i + 1} 完成`), Math.random() * 1000)
    )
  );
  
  const results = [];
  
  for await (const result of taskRunner(tasks, 3)) {
    results.push(result);
    console.log(`已完成: ${result}, 剩余并发: ${results.length}`);
  }
  
  return results;
}

5. WebSocket 消息流

async function* webSocketStream(url, protocols = []) {
  const ws = new WebSocket(url, protocols);
  
  yield new Promise((resolve, reject) => {
    ws.onopen = () => resolve('connected');
    ws.onerror = reject;
  });
  
  try {
    while (ws.readyState === WebSocket.OPEN) {
      const message = await new Promise((resolve, reject) => {
        ws.onmessage = (event) => resolve(event.data);
        ws.onerror = reject;
        ws.onclose = () => resolve(null);
      });
      
      if (message === null) break;
      yield JSON.parse(message);
    }
  } finally {
    if (ws.readyState === WebSocket.OPEN) {
      ws.close();
    }
  }
}

// 使用
async function handleWebSocket() {
  const stream = webSocketStream('wss://api.example.com/ws');
  
  try {
    console.log(await stream.next()); // 连接状态
    
    for await (const message of stream) {
      console.log('收到消息:', message);
      // 处理消息...
    }
  } catch (error) {
    console.error('WebSocket 错误:', error);
  }
}

错误处理

async function* safeGenerator() {
  try {
    yield await fetchData();
    yield await fetchMoreData();
  } catch (error) {
    console.error('生成器内部错误:', error);
    yield { error: error.message };
  } finally {
    console.log('清理资源...');
    // 清理逻辑
  }
}

// 外部错误处理
async function handleErrors() {
  const gen = safeGenerator();
  
  try {
    while (true) {
      const { value, done } = await gen.next();
      if (done) break;
      
      if (value.error) {
        // 处理错误结果
        handleError(value.error);
      } else {
        // 处理正常结果
        processData(value);
      }
    }
  } catch (error) {
    console.error('外部错误处理:', error);
  }
}

组合使用

// 多个异步生成器组合
async function* mergedStreams(...streams) {
  const promises = streams.map(async function* (stream) {
    for await (const item of stream) {
      yield item;
    }
  });
  
  // 合并所有流
  const allStreams = promises.flat();
  
  for await (const item of allStreams) {
    yield item;
  }
}

// 过滤异步流
async function* filterAsync(stream, predicate) {
  for await (const item of stream) {
    if (await predicate(item)) {
      yield item;
    }
  }
}

// 映射异步流
async function* mapAsync(stream, mapper) {
  for await (const item of stream) {
    yield await mapper(item);
  }
}

注意事项

  1. 必须使用 for await...of 或手动调用 next() 配合 await
  2. 返回的是 AsyncGenerator 对象,不是普通生成器
  3. 可以同时使用 awaityield
  4. 错误传播:内部错误会向外抛出
  5. 资源清理:使用 try...finally 确保资源释放

与普通生成器的比较

特性 普通生成器 异步生成器
声明 function* async function*
遍历 for...of for await...of
next() 返回 { value, done } Promise<{ value, done }>
可等待
主要用途 同步序列 异步序列/流

异步生成器函数在处理数据流、实时通信、分页数据等异步场景中非常有用,它们提供了一种简洁的方式来处理复杂的异步数据流。
生成器函数提供了强大的控制流管理能力,虽然在现代 JavaScript 中很多场景已被 async/await 替代,但在处理复杂的状态机、惰性计算、数据流处理等方面仍然非常有用。