我最近一直在挂这个话题。看起来AsyncIterables和Observables都具有类似流的质量,尽管它们的使用方式有所不同。
您可以使用这样的异步迭代器
const myAsyncIterable = async function*() { yield 1; yield 2; yield 3; }
const main = async () => {
for await (const number of myAsyncIterable()) {
console.log(number)
}
}
main()
您可以像这样食用可观察物
const Observable = rxjs
const { map } = rxjs.operators
Observable.of(1, 2, 3).subscribe(x => console.log(x))
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
我的首要问题基于此RxJS pr
如果可观察对象的发射速度快于循环完成的速度,则随着缓冲区变得更满,将建立内存。我们可以提供其他使用不同策略的方法(例如,仅使用最新值等),但将其保留为默认设置。请注意,循环本身可能有多个等待状态,这使问题更加严重。
在我看来,异步迭代器本来就没有背压问题,那么在Observable上实现Symbol.asyncIterator
(@@ asyncIterator)并默认为背压策略是否正确?根据AsyncIterables甚至需要Observables吗?
理想情况下,您可以通过代码示例向我展示AsyncIterables和Observables之间的实际区别。
答案 0 :(得分:2)
主要区别在于哪一方决定何时进行迭代。
对于异步迭代器,客户端通过调用await iterator.next()
来决定。消息源决定何时兑现承诺,但客户必须先要求下一个值。因此,使用者从源中“拉”数据。
Observables注册一个回调函数,当有新值传入时,可观察对象立即调用该回调函数。因此,源向使用者“推送”。
通过使用Subject并将其映射到异步迭代器的下一个值,可以很容易地使用Observable来消耗异步迭代器。每当您准备使用下一个项目时,就可以调用下一个主题。这是一个代码示例
const pull = new Subject();
const output = pull.pipe(
concatMap(() => from(iter.next())),
map(val => {
if(val.done) pull.complete();
return val.value;
})
);
//wherever you need this
output.pipe(
).subscribe(() => {
//we're ready for the next item
if(!pull.closed) pull.next();
});
答案 1 :(得分:0)
This是当前的实现Observable[Symbol.asyncIterator]
。
这是在数组上实现的Symbol.asyncIterator
的基本示例:
const dummyPromise = (val, time) => new Promise(res => setTimeout(res, time * 1000, val));
const items = [1, 2, 3];
items[Symbol.asyncIterator] = async function * () {
yield * await this.map(v => dummyPromise(v, v));
}
!(async () => {
for await (const value of items) {
console.log(value);
}
})();
/*
1 - after 1s
2 - after 2s
3 - after 3s
*/
我理解生成器( sync 生成器)的方式是它们是可暂停的函数,这意味着您可以立即请求一个值,然后在10秒后请求另一个值。异步生成器遵循相同的方法,除了它们生成的值是异步,这意味着您必须await
。
例如:
const dummyPromise = (val, time) => new Promise(res => setTimeout(res, time * 1000, val));
const items = [1, 2, 3];
items[Symbol.asyncIterator] = async function * () {
yield * await this.map(v => dummyPromise(v, v));
}
const it = items[Symbol.asyncIterator]();
(async () => {
// console.log(await it.next())
await it.next();
setTimeout(async () => {
console.log(await it.next());
}, 2000); // It will take 4s in total
})();
回到Observable
的实现:
async function* coroutine<T>(source: Observable<T>) {
const deferreds: Deferred<IteratorResult<T>>[] = [];
const values: T[] = [];
let hasError = false;
let error: any = null;
let completed = false;
const subs = source.subscribe({
next: value => {
if (deferreds.length > 0) {
deferreds.shift()!.resolve({ value, done: false });
} else {
values.push(value);
}
},
error: err => { /* ... */ },
complete: () => { /* ... */ },
});
try {
while (true) {
if (values.length > 0) {
yield values.shift();
} else if (completed) {
return;
} else if (hasError) {
throw error;
} else {
const d = new Deferred<IteratorResult<T>>();
deferreds.push(d);
const result = await d.promise;
if (result.done) {
return;
} else {
yield result.value;
}
}
}
} catch (err) {
throw err;
} finally {
subs.unsubscribe();
}
}
据我了解:
values
用于跟踪同步值
如果您有of(1, 2, 3)
,则values
数组将在到达[1, 2, 3]
之前包含while(true) { }
。而且由于您使用的是for await (const v of ...)
,
您将像在做it.next(); it.next(); it.next() ...
一样请求值。
换句话说,一旦您可以从迭代器中使用一个值,就立即请求下一个值,直到数据生成器无所提供为止。
deferreds
用于异步值
因此,在您的第一个it.next()
处,values
数组为空(这意味着该可观察对象没有同步发出),因此它将退回到最后一个else
,这只是创建了一个承诺会被添加到deferreds
,然后await
兑现承诺,直到resolves
或rejects
。
当可观察对象最终发出时,deferreds
将不会为空,因此等待的诺言将resolve
与新到达的值一起出现。
const src$ = merge(
timer(1000).pipe(mapTo(1)),
timer(2000).pipe(mapTo(2)),
timer(3000).pipe(mapTo(3)),
);
!(async () => {
for await (const value of src$) {
console.log(value);
}
})();