Loading [MathJax]/jax/output/CommonHTML/config.js
社区首页 >问答首页 >每小时发射一次的RxJS无穷大流

每小时发射一次的RxJS无穷大流
EN

Stack Overflow用户
提问于 2020-12-03 02:26:41
回答 5查看 969关注 0票数 1

我需要构建一个流,它在调用后立即发出api请求,如果页面没有刷新,则每小时(下午1:00,下午2:00 )。我用setTimeout()构建它,但是我想用RxJ实现它。你能帮帮我吗?

代码语言:javascript
代码运行次数:0
复制
isUpdated() {
    return new Observable<Object>(function update(obs) {
        this.http.get(`/update`).subscribe(data => {
            obs.next(data);
        });
        const timer = (60 - new Date().getMinutes()) * 60 * 1000;
        setTimeout(() => update.call(this, obs), timer);
    }.bind(this));
}

//call
isUpdated().subscribe(data => console.log(data));
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2020-12-03 07:01:03

我认为要解决这个问题,你需要把它分解成更小的部分。

首先,我们知道,在某个时候,根据当前时间,我们想知道何时触发下一个调用。如果我们得到一个时间戳,它给出了ms中的当前时间,并且我们希望在下一个小时之前得到ms的数量,那么我们可以这样做:

代码语言:javascript
代码运行次数:0
复制
const timeToNextHourInMs = (currentTimestampMs) => {
  const timestampSeconds = currentTimestampMs / 1000;

  const numberOfSecondsIntoTheCurrentHour = timestampSeconds % 3600;

  const numberOfSecondsToTheNextHour = 3600 - numberOfSecondsIntoTheCurrentHour;

  return numberOfSecondsToTheNextHour * 1000;
};

我希望变量名足够明确,我不需要评论,但让我知道否则。

接下来,我们要处理流问题:

  • 我们想立即触发一个HTTP调用
  • 立即获取发出的值。
  • 每次新的一小时开始(1:00,2:00,3:00等),都要做上述所有的事情。

以下是你如何做到的:

代码语言:javascript
代码运行次数:0
复制
this.http.get(`/update`).pipe(
  timestamp(),
  switchMap(({ timestamp, value }) =>
    concat(
      of(value),
      EMPTY.pipe(delay(timeToNextHourInMs(timestamp)))
    )
  ),
  repeat()
);

让我们来看看上面的逻辑:

  • 首先,我们立即进行HTTP调用
  • 完成HTTP调用后,我们将得到当前的时间戳(稍后根据该时间戳确定我们何时要执行下一次调用)
  • 我们执行一个switchMap,但是由于HTTP调用只返回1值,所以在这个非常特殊的情况下它并不重要。我们也可以用flatMapconcatMap
  • switchMap内部,我们首先使用concat发送我们刚刚从HTTP调用中得到的值,但也让这个可观察到的值一直保持到当前our的末尾(通过使用我们先前创建的函数)。
  • 因此,在当前小时结束时,流将完成。但是,由于我们有了一个retry,一旦流完成,我们将再次订阅它(并且提醒您,流只在新的一个小时开始时才会完成!)

我建议在这里添加一件事情,但这并不是最初问题的一个要求,那就是要进行一些错误处理,以便当您进行调用时出现问题时,它会在几秒钟后自动重试。否则,想象一下,当轮询开始时,如果您的网络在那个时候不工作5s,那么您的流就会立即出错。

为此,您可以引用此精彩的回答,并在可重用的自定义操作符中这样做:

代码语言:javascript
代码运行次数:0
复制
const RETRY_DELAY = 2000;
const MAX_RETRY_FOR_ONE_HTTP_CALL = 3;

const automaticRetry = () => (obs$) =>
  obs$.pipe(
    retryWhen((error$) =>
      error$.pipe(
        concatMap((error, index) =>
          iif(
            () => index >= MAX_RETRY_FOR_ONE_HTTP_CALL,
            throwError(error),
            of(error).pipe(delay(RETRY_DELAY))
          )
        )
      )
    )
  );

这将在每次重试之间延迟三次可观察到的重试。3次之后,流将抛出最后发出的错误,从而出错。

现在,我们只需将这个自定义操作符添加到流中:

代码语言:javascript
代码运行次数:0
复制
this.http.get(`/update`).pipe(
  automaticRetry(),
  timestamp(),
  switchMap(({ timestamp, value }) =>
    concat(
      of(value),
      EMPTY.pipe(delay(timeToNextHourInMs(timestamp)))
    )
  ),
  repeat()
);

我还没有对上面的代码进行实际测试,所以请在您这边这样做,并让我知道它是如何进行的。但如果我的逻辑是正确的,下面是事情的发展方向:

  • 假设你在2:40启动你的应用程序
  • 立即发出HTTP调用
  • 你很快就会得到回应
  • 这条溪流将保持20 be的开放状态。
  • 3:00,流完成,重试启动:我们执行另一个HTTP调用
  • 这一次,服务器重新部署,几秒钟内无法使用。
  • 在内部,流错误,但是由于我们的自定义操作符automaticRetry,它等待了3秒,然后重新尝试了一次,仍然什么也没有。它再等待3秒,这一次很好,结果会被传递到下游
  • 无限期重复:)

让我知道是怎么回事

票数 1
EN

Stack Overflow用户

发布于 2020-12-03 02:44:16

也许您可以这样做(我没有测试这段代码):

代码语言:javascript
代码运行次数:0
复制
import { merge, concat, of, timer, interval } from 'rxjs';
import { switchMap } from 'rxjs/operators';

...

isUpdated() {
  return concat(
    merge(
      of(null), // make call inmediately
      timer((60 - new Date().getMinutes()) * 60 * 1000), // the next full hour eg. 2:00
    ),
    interval(60 * 60 * 1000), // every hour
  ).pipe(
    switchMap(() => this.http.get(...)),
  )
})

concat只有在第一个merge完成后才会订阅interval,这是在下一个完整的小时(例如: 2:00 ),然后每小时发出一次。

@MrkSef有一个很好的想法,它可以简化成这样的东西:

代码语言:javascript
代码运行次数:0
复制
isUpdated() {
  return timer(
    (60 - new Date().getMinutes()) * 60 * 1000, // the next full hour eg. 2:00
    60 * 60 * 1000 // every hour
  ).pipe(
    startWith(null), // initial request
    switchMap(() => this.http.get(...)),
  )
})
票数 1
EN

Stack Overflow用户

发布于 2020-12-03 08:28:36

我觉得很简单。

计时器接受两个参数,第一个参数是第一个呼叫的延迟,然后是调用的间隔。你的下一个小时的偏移是正确的,我用你的代码。

使用startWith(null)立即启动。

代码语言:javascript
代码运行次数:0
复制
const startDelayMs = (60 - new Date().getMinutes()) * 60 * 1000;
const hourMs = 60 * 60 * 1000;

timer(startDelayMs, hourMs).pipe(
   startWith(null),
   switchMap(()) // do your call here
)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65124122

复制
相关文章
将字符串转换为date类型_java字符串转date类型
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/09
14.1K0
C语言中把数字转换为字符串 【转】
在将各种类型的数据构造成字符串时,sprintf 的强大功能很少会让你失望。由于sprintf 跟printf 在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。这也导致sprintf 比printf 有用得多。 sprintf 是个变参函数,定义如下: int sprintf( char *buffer, const char *format [, argument] ... ); 除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数: 格式化字符串上。 printf 和sprintf 都使用格式化字符串来指定串的格式,在格式串内部使用一些以“%”开头的格式说明符(format specifications)来占据一个位置,在后边的变参列表中提供相应的变量,最终函数就会用相应位置的变量来替代那个说明符,产生一个调用者想要 的字符串。 格式化数字字符串 sprintf 最常见的应用之一莫过于把整数打印到字符串中,所以,spritnf 在大多数场合可以替代 itoa。 如: //把整数123 打印成一个字符串保存在s 中。 sprintf(s, "%d", 123); //产生"123" 可以指定宽度,不足的左边补空格: sprintf(s, "%8d%8d", 123, 4567); //产生:" 123 4567" 当然也可以左对齐: sprintf(s, "%-8d%8d", 123, 4567); //产生:"123 4567" 也可以按照16 进制打印: sprintf(s, "%8x", 4567); //小写16 进制,宽度占8 个位置,右对齐 sprintf(s, "%-8X", 4568); //大写16 进制,宽度占8 个位置,左对齐 这样,一个整数的16 进制字符串就很容易得到,但我们在打印16 进制内容时,通常想要一种左边补0 的等宽格式,那该怎么做呢?很简单,在表示宽度的数字前面加个0 就可以了。 sprintf(s, "%08X", 4567); //产生:"000011D7" 上面以”%d”进行的10 进制打印同样也可以使用这种左边补0 的方式。 这里要注意一个符号扩展的问题:比如,假如我们想打印短整数(short)-1 的内存16 进制表示形式,在Win32 平台上,一个short 型占2 个字节,所以我们自然希望用4 个16 进制数字来打印它: short si = -1; sprintf(s, "%04X", si); 产 生“FFFFFFFF”,怎么回事?因为spritnf 是个变参函数,除了前面两个参数之外,后面的参数都不是类型安全的,函数更没有办法仅仅通过一个“%X”就能得知当初函数调用前参数压栈时被压进来的到底 是个4 字节的整数还是个2 字节的短整数,所以采取了统一4 字节的处理方式,导致参数压栈时做了符号扩展,扩展成了32 位的整数-1,打印时4 个位置不够了,就把32 位整数-1 的8 位16 进制都打印出来了。 如果你想看si 的本来面目,那么就应该让编译器做0 扩展而不是符号扩展(扩展时二进制左边补0 而不是补符号位): sprintf(s, "%04X", (unsigned short)si); 就可以了。或者: unsigned short si = -1; sprintf(s, "%04X", si); sprintf 和printf 还可以按8 进制打印整数字符串,使用”%o”。注意8 进制和16 进制都不会打 印出负数,都是无符号的,实际上也就是变量的内部编码的直接的16 进制或8 进制表示。 控制浮点数打印格式 浮点数的打印和格式控制是sprintf 的又一大常用功能,浮点数使用格式符”%f”控制,默认保 留小数点后6 位数字,比如: sprintf(s, "%f", 3.1415926); //产生"3.141593" 但有时我们希望自己控制打印的宽度和小数位数,这时就应该使用:”%m.nf”格式,其中m 表 示打印的宽度,n 表示小数点后的位数。比如: sprintf(s, "%10.3f", 3.1415626); //产生:" 3.142" sprintf(s, "%-10.3f", 3.1415626); //产生:"3.142 " sprintf(s, "%.3f", 3.1415626); //不指定总宽度,产生:"3.142" 注意一个问题,你猜 int i = 100; sprintf(s, "%.2f", i); 会打出什么东东来?“100.00”?对吗?自己试试就知道了,同时也试试下面这个: sprintf(s, "%.2f", (double)i); 第 一个打出来的肯定不是正确结果,原因跟前面提到的一样,参数压栈时调用者并不知道跟i相对应
用户2038589
2018/09/06
16.8K0
用FastJson将JSON字符串转Json[通俗易懂]
JSON:fastJson的解析器,用于JSON格式字符串与JSON对象及javaBean之间的转换。 JSONObject:fastJson提供的json对象。 JSONArray:fastJson提供json数组对象。
全栈程序员站长
2022/11/10
3K0
c++如何将字符串转为数组(将字符串转换为数组)
string [] imgArr=imgData.Split(new char[]{‘,’});
全栈程序员站长
2022/07/29
7K0
c# 将字符串转换为指定类型的值
private object GetValueByProperty(string key, string value, ref Type typeValue) { Type t = typeof(T); var property = t.GetProperty(key); if (property == null) { return value;
冰封一夏
2019/09/11
3.1K0
将tensor转换为图像_tensor转int
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/07
11.4K0
mysql整型转字符串_java中如何将字符串转换为字符数组
select * from A order by cast(name as unsigned);
全栈程序员站长
2022/09/27
23.3K0
java将字符串转换为json对象的方法_java jsonobject转string
在与服务器交互的时候,我们往往会使用json字符串,今天的例子是java对象转化为字符串,
全栈程序员站长
2022/11/08
21.2K0
c语言字符串转换为整型_c语言输出负数用什么
注意:整型变量与字符变量相加减是使用ASCII码值,可以通过类型转换或格式字符来控制打印。
全栈程序员站长
2022/11/02
2.2K0
Python将字符串转换为列表
We can convert a string to list in Python using split() function.
全栈程序员站长
2022/09/06
6K0
用Python将图片转换为base64字符串
无他,这篇博文记录一下利用Python将OpenCV图片转换为base64字符串并在网页上进行展示的过程,权当备忘。可在这里查看源码。
王云峰
2023/10/23
6760
Linux 将Shell脚本转换为C
默认的shell脚本是不能够加密的,放出来的都是源代码,如果需要对代码进行加密操作,那么可以使用如下工具试试。
微软技术分享
2022/12/28
1.4K0
[1154]如何将字符串转换为datetime
1.把datetime转成字符串: 2017-11-23 17:05:18 2.把字符串转成datetime: 2017-11-23 16:10:10 3.把字符串转成时间戳形式: 1511424610.0 4.把时间戳转成字符串形式: 2017-11-23 17:05:18 5.把datetime类型转外时间戳形式: 1511427918.0
周小董
2022/07/27
3.3K0
C#将汉字转换为拼音
明志德道
2023/10/21
2840
C#将汉字转换为拼音
int转换为char数组_C语言将整数转化为字符串
如int i=1;在程序中直接将强制将i转换成char类型char a=(char)i,会发现a并不是’1’而是’\0001′,原因是在将i转换成char时,默认的会把i的值当成ASCII值,这样a的值就是’\0001’了
全栈程序员站长
2022/11/03
3.3K0
JavaSE-将字符串转换为数字
提示:仔细思考所有可能的输入情况。这个问题没有给出输入的限制,你需要自己考虑所有可能的情况。
程序员阿杜
2021/03/15
2.5K0
JavaSE-将字符串转换为数字
JavaSE-将字符串转换为数字
提示:仔细思考所有可能的输入情况。这个问题没有给出输入的限制,你需要自己考虑所有可能的情况。
程序员阿杜
2021/04/07
2.4K0
C# 枚举转字符串 枚举转字符串字符串转枚举
如果把一个枚举转字符串,那么如何把字符串转枚举?可以使用 Enum.Parse 不过这个方法可以会抛异常,所以使用需要知道字符串是可以转
林德熙
2018/09/18
4K0
C# byte[]转换为字符串
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp4 { class Program { static void Main(string[] args) { byte[] byt1 = { 0x01, 0x11, 0
zls365
2020/08/19
1.6K0
C# byte[]转换为字符串
json字符串转换为Json对象_前端字符串转json
参考网上的文章,做了一个关于json的总结,进行留存帮助以后阅读,希望可以帮助到大家。
全栈程序员站长
2022/09/28
7.6K0

相似问题

谷歌计算引擎支持HTTPS吗?

12

谷歌云计算引擎

116

谷歌云计算引擎sshKeys移除

10

谷歌云计算引擎不工作

11

谷歌云计算引擎- Nodejs无法工作

140
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档