深入了解Rx.ReplaySubject:如何延迟`next()`?

3

忽略块: 要么我犯了错误,要么威士忌开始起作用了。(也不能排除我变傻的可能性。对此我很抱歉)

期望结果:

我原本希望 ReplaySubject 每2秒钟返回一个值,因为在调用 next() 之前每次等待两秒钟。

结果如下:

结果是等待了2秒钟,但所有的值同时输出

相关代码如下:

import { ReplaySubject } from 'rxjs';

export const rs$: ReplaySubject<number> = new ReplaySubject();

rs$.subscribe({
  next: (data) => console.log(data),
  error: (error) => console.warn(error),
  complete: () => console.log('ReplaySubject completed'),
});

const fakeAPIValuesOne: Array<number> = [7, 11, 13];

fakeAPIValuesOne.forEach(async (entry: number) => {
  await wait(2000);  // <--- Why does wait() not work?
  rs$.next(entry);
});

function wait(milliseconds: number) {
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
}

问题:

我在这里做了什么根本性的错误?

如果您想尝试一下:https://stackblitz.com/edit/rxjs-wpnqvq?file=index.ts

编辑1:

setTimeout 也没有效果。 下面的代码与上面完全相同:

fakeAPIValuesOne.forEach((value: number) => {
  setTimeout(() => {
    rs$.next(value);
  }, 2000);
});

我想知道 next() 如何覆盖这里的所有延迟?

编辑 2

问题已解决,正确答案已标记,谢谢! 您需要以下细节来运行您的 ts 文件的根级别等待。

package.json

请注意type部分:

{
  "name": "playground",
  "version": "1.0.0",
  "description": "",
  "main": "index.ts",
  "scripts": {
    "start": "nodemon index.ts",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "rxjs": "^7.5.5",
    "ts-node": "^10.7.0",
    "typescript": "^4.8.0-dev.20220507"
  },
  "type": "module"
}

nodemon.json

请考虑以下配置以避免错误:TypeError [ERR_UNKNOWN_FILE_EXTENSION]: 未知的文件扩展名“.ts”

{
  "execMap": {
    "ts": "node --loader ts-node/esm"
  }
}

最后但并非最不重要的是 tsconfig.json
{
  "compilerOptions": {
    "module": "ESNext",
    "target": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "isolatedModules": true,
    "noEmit": true,
    "strict": true,
    "lib": ["esnext", "DOM"]
  }
}

一天之后:绝对不在高峰期内。https://imgs.xkcd.com/comics/ballmer_peak.png - user3025289
2个回答

1
以下内容摘自mozilla web docs(链接在此处)

注意: forEach需要同步函数。

forEach不等待承诺。在使用承诺(或异步函数)作为forEach回调时,请确保您知道其含义。

因此,这个问题与ReplaySubject无关,您不能在这种情况下使用forEach
祝好! 编辑:已解决
import { ReplaySubject } from "rxjs";

export const rs$ = new ReplaySubject();

rs$.subscribe({
  next: (data) => console.log(data),
  error: (error) => console.warn(error),
  complete: () => console.log("ReplaySubject completed"),
});

const fakeAPIValuesOne = [7, 11, 13];

// That won't work:
// fakeAPIValuesOne.forEach(async (entry: number) => {
//   await wait(2000);
//   rs$.next(entry);
// });


// That will work
for (const element of fakeAPIValuesOne) {
  await wait(2000);
  rs$.next(element);
}

function wait(milliseconds: number) {
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
}

0

我认为值得一提的是,你通常不应该期望这个能够正常工作。

考虑以下情况:

wait(2000);
wait(2000);
wait(2000);
console.log("Hello");

"

“Hello”将立即打印到控制台。它不会等待6秒钟。

如果将它们放在循环中,这并不会改变。

"
for (const n of [1,2,3]) {
  wait(2000);
}
console.log("Hello");

这也会立刻打印出"Hello"。


如果您不使用.then()await,标准行为是您不会等待。一些API将(当然)代表您执行等待,但预期的行为是它不会这样做。
您需要像这样编写它:
await wait(2000);
await wait(2000);
await wait(2000);
console.log("Hello");

现在你需要等待6秒钟,然后才能将“Hello”打印到控制台。


现在看看这个:

const createPromise = async (v) => {
   await wait(2000);
   console.log("Hello From createPromise: ", v);
}

createPromise(1);
createPromise(2);
createPromise(3);
console.log("Hello");

你会注意到这并没有什么不同。它会立即打印“Hello”,然后2秒钟后所有的承诺都将被解决并打印。

Hello
// 2 seconds wait
Hello From createPromise: 1
Hello From createPromise: 2
Hello From createPromise: 3

你会注意到它们之间没有相互等待。这是 Promise 的预期行为。你需要使用 await.then 来实际等待 Promise 的结果。

你需要编写:

await createPromise(1);
await createPromise(2);
await createPromise(3);
console.log("Hello");

这两个循环执行相同的操作(不出所料,它们不会等待)

for (const n of [1,2,3]) {
  createPromise(n);
}

[1,2,3].forEach(n => createPromise(n))

console.log("Hello");

再次强调,你会看到打印的第一件事是"Hello",因为你从未等待这些承诺中的任何一个解决。

你等待内部的wait(2000)承诺,但你从未等待外部的createPromise()承诺。所以当然你不会等待它们 :)

for (const n of [1,2,3]) {
  await createPromise(n);
}

1
谢谢你,是的你说得对,我承认那不是我最伟大的时刻。 - user3025289

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接