AsyncIterable和Observable之间的实际区别是什么?

时间:2020-06-10 19:51:03

标签: javascript ecmascript-6 promise rxjs observable

我最近一直在挂这个话题。看起来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之间的实际区别。

2 个答案:

答案 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兑现承诺,直到resolvesrejects

    当可观察对象最终发出时,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);
  }
})();

StackBlitz

相关问题