原生JavaScript中Generator的yield/next机制本质是手动调度的单向暂停恢复模式,非真正双向通信;其next参数仅影响前一个yield返回值,首次调用被忽略;通过封装可构建可读写响应式数据节点及订阅通知底座。

原生 JavaScript 中没有内置的 yield 和 next() 双向通信机制——因为 Generator 函数的 next() 方法只能单向传入值,不能“主动推”数据给外部调用者。所谓“yield 配合 next 传参实现双向通信”,本质是利用 Generator 的暂停/恢复特性 + 手动调度,构建一个可读写、可响应的数据流协调层。它不是魔法,而是一种模式封装。
理解 yield / next 的真实行为边界
Generator 每次执行到 yield 就暂停,并把右侧表达式的值返回给外部;外部调用 next(value) 时,这个 value 会作为上一次 yield 表达式的返回值(即进入函数体内部继续执行时,yield 左侧接收该值)。但注意:
-
next()的参数只影响 前一个 yield 的返回值,对当前 yield 无直接影响 - Generator 本身不持有状态监听器,也不自动触发更新,需手动桥接
- 首次
next()传参会被忽略(规范要求),必须用next()或next(undefined)启动
用 Generator 封装一个可读写的响应式数据节点
核心思路:让 Generator 成为“数据中枢”,每次 yield 暴露当前值,同时用 next(input) 接收新值并触发同步更新或副作用。
function* reactiveCell(initial) {
let value = initial;
while (true) {
// 暂停并输出当前值,等待外部输入
const input = yield value;
if (input !== undefined) {
value = input; // 接收新值
console.log('✅ 数据已更新为:', value);
// 这里可触发 DOM 更新、事件广播、依赖通知等
}
}
}
<p>// 使用示例
const cell = reactiveCell(0);
console.log(cell.next().value); // 0(首次启动)
console.log(cell.next(10).value); // 10(传入新值,返回更新后值)
console.log(cell.next(20).value); // 20</p>
扩展成支持订阅与自动响应的通信底座
仅靠单个 Generator 不足以构成“底座”。需引入观察者模式,让多个消费者能监听变化,并允许外部主动写入:
class ReactiveChannel {
constructor(initial) {
this.subscribers = new Set();
this.generator = this.#run(initial);
this.current = initial;
}
<p>*#run(initial) {
let val = initial;
while (true) {
const input = yield val;
if (input !== undefined) {
val = input;
this.current = val;
this.#notify(val);
}
}
}</p><h1>notify(val) {</h1><pre class="brush:php;toolbar:false;">this.subscribers.forEach(cb => cb(val));}
get() { return this.current; }
set(value) { this.generator.next(value); }
subscribe(cb) { this.subscribers.add(cb); cb(this.current); // 立即通知当前值 return () => this.subscribers.delete(cb); } }
// 使用 const counter = new ReactiveChannel(0);
counter.subscribe(v => console.log('? 监听到:', v)); counter.set(5); // ? 监听到: 5 counter.set(12); // ? 监听到: 12
实际集成 DOM 更新的小型实践示例
结合原生 DOM,让 Generator 控制视图刷新节奏,避免频繁重绘:
function* renderLoop(initialText) {
let text = initialText;
const el = document.getElementById('app');
<p>while (true) {
el.textContent = text;
const nextText = yield text;
if (nextText !== undefined) text = nextText;
}
}</p><p>const renderer = renderLoop('Loading...');
renderer.next(); // 初始化渲染</p><p>// 外部可随时驱动更新
setTimeout(() => renderer.next('Ready!'), 1000);
setTimeout(() => renderer.next('Done.'), 2000);</p>
这种结构不依赖框架,轻量可控,适合嵌入微前端、配置驱动 UI 或状态协同场景。关键不在“用没用 yield”,而在于是否把数据变更、消费响应、外部驱动三者通过 Generator 的暂停点自然对齐。不复杂但容易忽略细节。











