不使用concatMap如何实现Observable延迟?所有值发出后整体延迟接收
解决方案
核心思路:我们需要延迟的是整个流的完成后结果推送时机,而非延迟每个值的发射过程,以下两种方案均不使用concatMap即可满足需求。
注意:原
generateData方法的返回类型标注有误,实际返回的是Observable<[number, number, number]>类型,不是number[]。
原方法代码修正后参考:
public generateData(baseval, count, yrange): Observable<[number, number, number]> { let i = 0; let series = []; while (i < count) { let x = Math.floor(Math.random() * (750 - 1 + 1)) + 1; let y = Math.floor(Math.random() * (yrange.max - yrange.min + 1)) + yrange.min; let z = Math.floor(Math.random() * (75 - 15 + 1)) + 15; series.push([x, y, z]); baseval += 86400000; i++; } return from(series); }
方案1:接收完整结果数组(常用场景)
如果最终需要一次性拿到所有生成的数据,使用toArray + delay组合即可:
import { delay, toArray } from 'rxjs/operators'; // 调用示例,延迟1000ms接收结果 const data$ = generateData(Date.now(), 10, { min: 20, max: 200 }).pipe( // 收集源发射的所有值,等源完全结束后才输出数组 toArray(), // 延迟数组的发射时机,可自定义时长 delay(1000) ); // 订阅获取结果 data$.subscribe(allData => { console.log('延迟后拿到的全部数据:', allData); });
原理说明:
from(series)会同步发射所有生成的三元组数据,没有任何延迟toArray会将所有发射的值缓存,直到源流complete后,才把缓存的所有值组成的数组作为单个值输出delay仅延迟这个数组的输出时机,不会对单个值的生成过程添加任何延迟,完全符合要求。
方案2:保持单个值依次发射
如果需要保留每个值单独发射的形式,不打包为数组,可以使用materialize + delayWhen + dematerialize组合:
import { materialize, delayWhen, dematerialize, timer, EMPTY } from 'rxjs'; const data$ = generateData(Date.now(), 10, { min: 20, max: 200 }).pipe( // 将流的所有next、complete、error通知转为标准化的Notification对象 materialize(), // 仅对complete通知做延迟,其余通知直接透传 delayWhen(notification => notification.kind === 'C' ? timer(1000) : EMPTY), // 将Notification对象还原为正常的流通知 dematerialize() ); // 订阅会依次收到每个单独的三元组数据 data$.subscribe(singleData => { console.log('收到单个数据:', singleData); });
原理说明:
- 所有next通知会先被缓存,不会延迟单个值的生成
- 等到源流触发complete后,等待指定的延迟时间,才会把缓存的所有next通知按原顺序同步发射,同时触发complete
- 单个值本身没有添加任何延迟,只是整体的发射时机被推迟到所有值生成完成且延迟时间结束后。
内容的提问来源于stack exchange,提问作者Bidisha Das
相关产品推荐
相关产品推荐

