You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RxJS中lastValueFrom结合scheduled与asapScheduler的异常行为

RxJS scheduled+asapScheduler与lastValueFrom结合的异常行为分析

我编写了一段测试scheduled/asapScheduler的脚本,发现其与lastValueFrom结合时存在异常行为:

import { of, lastValueFrom, scheduled, asapScheduler } from 'rxjs';

const bbj = { foo: 'bar' };

async function foo() {
  const result = await lastValueFrom(scheduled(of(bbj), asapScheduler));
  console.log(result);
  return result;
}

async function main() {
  await foo(); // 第1次控制台日志
  await foo(); // 第2次控制台日志
  await foo(); // 第3次控制台日志
}

document.getElementById('button').addEventListener('click', function () {
  main();
});

预期行为:点击按钮时控制台输出3次日志:

{foo: "bar"}
{foo: "bar"}
{foo: "bar"}

实际行为:第一次点击无日志;第二次点击输出5次;第三次点击仅输出1次。


问题原因

核心矛盾在于asapScheduler的微任务调度特性与async/await的等待逻辑冲突:

  • asapScheduler会将任务安排在当前同步代码执行完毕后、微任务队列清空前执行,属于微任务范畴。
  • 第一次点击时,main()触发的三个foo()会依次订阅Observable,但这些订阅产生的微任务会被推入同一个队列。由于lastValueFrom需要等待Observable完成,而asapScheduler的任务延迟执行,导致第一个foo()的await还在等待时,后续foo()已经开始订阅,Observable的完成信号与微任务队列的执行顺序混乱,第一次的日志被积压未输出。
  • 第二次点击时,之前积压的微任务与新任务叠加,导致日志数量异常;第三次点击时积压任务处理完毕,又因调度顺序问题只输出1次。

本质是asapScheduler的微任务调度和async/await的异步等待逻辑结合时,Observable的订阅、执行、完成时机出现了交叉混乱,破坏了预期的顺序执行逻辑。


修复方案

有两种简单有效的修复方式:

1. 替换为asyncScheduler

asyncScheduler将任务安排到下一个事件循环的宏任务队列,与async/await的等待逻辑兼容,能保证每个foo()按顺序执行并输出日志:

import { of, lastValueFrom, scheduled, asyncScheduler } from 'rxjs';

const bbj = { foo: 'bar' };

async function foo() {
  const result = await lastValueFrom(scheduled(of(bbj), asyncScheduler));
  console.log(result);
  return result;
}

async function main() {
  await foo();
  await foo();
  await foo();
}

document.getElementById('button').addEventListener('click', function () {
  main();
});

2. 移除scheduled包装

如果不需要专门测试调度器,直接使用of(bbj)即可,它本身是同步执行的,与lastValueFrom结合不会有调度顺序问题:

import { of, lastValueFrom } from 'rxjs';

const bbj = { foo: 'bar' };

async function foo() {
  const result = await lastValueFrom(of(bbj));
  console.log(result);
  return result;
}

async function main() {
  await foo();
  await foo();
  await foo();
}

document.getElementById('button').addEventListener('click', function () {
  main();
});

内容的提问来源于stack exchange,提问作者Simone Nigro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 15:38:09