Rxjs的interval和timer与Js的setInterval和setTimeout

Rxjs的interval和timer与Js的setInterval和setTimeout定时器?setTimeout?setInterval?interval?timer?宏任务?微任务?

1、从JavaScript上理解setInterval和setTimeout

它们的共同属性:

  • 函数第一参数:被执行的函数或一个被执行的代码串。
  • 函数第二参数:延迟时间,毫秒,默认0。
  • timeoutID:定时器ID,在clearTimeout()函数中被用来清除指定id定时器。
  • 清除定时器:,setInterval对应clearInterval(),setTimeout对应clearTimeout()

1.1 setTimeout

  • setTimeout:指定毫秒数后调用函数或计算表达式,只执行一次也就是定时执行。

🐂 setTimout的基本用法

setTimeout("alert('对不起, 三秒钟已到')", 3000);
setTimeout(function f () {
      alert("对不起, 三秒钟已到");
}, 3000);
function f () {
   alert("对不起, 三秒钟已到");
}
setTimeout(f, 3000);

🐂理解setTimeout的特点

setTimeout是单线程,类似异步,但不是异步。注意地,回调函数不一定是异步,异步一定有回调函数。下面例子为什么打印了6个6?

从运行机制上理解,因为for循环长度是0到5,js的运行机制是单线程的,代码执行是同步执行,执行到for的时候遇到了setTimeout,由于时间还没到,所以回调函数在等待,而是执行i递增,i=6时候for代码执行完毕,此时再去执行setTimeout的回调函数,由于var定义的是全局变量,所以当前读取到就是i=6。

从作用域上理解,var定义的i是for函数的全局变量,每次递增不会重新声明,由于每次递增没有发现i声明,但是for的全局存在i,此时会通过作用域链去找i,找到的时候for循环已经执行完毕,退出循环了。

  for (var i = 0; i <= 5; i++) {
      setTimeout(function f () {
        console.log(i);
      }, i * 100);
  }
  //打印了6个6

也许你还会疑惑退出循环了,为什么还会打印?下面例子可以说明这点,打印了6个定时器id,每个setTimeout内找i时是需要时间的,找到i的时候for循环已经结束了。

for (var i = 0; i <= 5; i++) {
      let timer = setTimeout(function f () {
        console.log(i);
      }, i * 100);
      console.log(timer);//6个定时器1 2 3 4 5 6
}
  • 解决方法1:直接获取当前作用域的i作为块级作用域
for (var i = 0; i <= 5; i++) {
      let timer = i;
      setTimeout(function f () {
        console.log(timer);
      }, i * 100);
}
  • 解决方法2:let指定块级作用域,每一次递增都会重新定义i。
for (let i = 0; i <= 5; i++) {
      setTimeout(function f () {
        console.log(i);
      }, i * 100);
}
  • 解决方法3:立即执行函数
for (var i = 0; i <= 5; i++) {
      (function time (i) {
        setTimeout(function f () {
          console.log(i);
        }, i * 100);
      })(i);
}

🐂 setTimeout经典栗子

  • 栗子1:倒计时
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
</head>

<body>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
    <script> let times = [{ time: "2022/12/5 23:07:00", }, { time: "2022/12/5 23:08:00", }, { time: "2022/12/5 23:09:00", }, { time: "2022/12/5 23:10:00", },]; function formatTime (n) { return n >= 0 && n < 10 ? "0" + n : "" + n; } const backTime = (t, index) => { var oSpan = document.getElementsByTagName(`div`)[index + 0]; var timer = null; function fn () { let current = moment(new Date()); //获取时 let end = moment(new Date(t)); //结束时间 diff = end.diff(current); //获取毫秒时间差 if (diff <= 0) { clearTimeout(timer); return; } var transecond = Math.round(diff / 1000); //转化为秒 var day = Math.floor(transecond / 3600 / 24); //获取天数 var hour = Math.floor((transecond / 3600) % 24); //获取小时,取余代表不满一天那部分 var minute = Math.floor((transecond / 60) % 60); //获取分,取余代表不满小时那部分 var second = transecond % 60; var str = formatTime(day) + '<span class="time">天</span>' + formatTime(hour) + '<span class="time">时</span>' + formatTime(minute) + '<span class="time">分</span>' + formatTime(second) + '<span class="time">秒</span>'; oSpan.innerHTML = str; timer = setTimeout(fn, 1e3); } fn(); }; times.forEach((item, index) => { backTime(item.time, index); }); </script>
</body>
</html>

1.2 setInterval

  • setInterval:指定毫秒数间隔调用函数或计算表达式,执行多次就是循环执行。

       一般不建议使用setInterval
       setInterval的缺点:
    
       1.无视代码错误:如果setInteral执行的代码由于某种原因出了错,它还会持续不断地调用该代码。
       
       2.无视网络延迟:假设你每隔一段时间就通过Ajax轮询一次服务器,由于某些原因(服务器过载、临时断网、流量剧增、用户带宽受限)等,你请求要花地时间远比你想象地要长。但setInterval会次序不断地触发请求,最终你的客户端网络队列塞满Ajax调用。
       
       3.不保证执行:并不能保证到了时间间隔,代码就能执行。也许你的有些函数需要花很长时间才能完成,那某些调用会被直接忽略。
       
    

🐂 setInterval经典栗子

  • 栗子1:倒计时
let times = [{
    time: "2022/12/5 23:07:00",
}, {
    time: "2022/12/5 23:08:00",
}, {
    time: "2022/12/5 23:09:00",
}, {
    time: "2022/12/5 23:10:00",
},];

function formatTime (n) {
    return n >= 0 && n < 10 ? "0" + n : "" + n;
}

const backTime = (t, index) => {
    var oSpan = document.getElementsByTagName(`div`)[index];
    var timer = null;

    function fn () {
        let current = moment(new Date()); //获取时
        let end = moment(new Date(t)); //结束时间
        diff = end.diff(current); //获取毫秒时间差
        if (diff <= 0) {
            /* 区别在这!*/
            clearInterval(timer);
            oSpan.innerHTML = '已结束!';
            return;
        }
        var transecond = Math.round(diff / 1000); //转化为秒
        var day = Math.floor(transecond / 3600 / 24); //获取天数
        var hour = Math.floor((transecond / 3600) % 24); //获取小时,取余代表不满一天那部分
        var minute = Math.floor((transecond / 60) % 60); //获取分,取余代表不满小时那部分
        var second = transecond % 60;
        var str =
            formatTime(day) +
            '<span class="time">天</span>' +
            formatTime(hour) +
            '<span class="time">时</span>' +
            formatTime(minute) +
            '<span class="time">分</span>' +
            formatTime(second) +
            '<span class="time">秒</span>';
        oSpan.innerHTML = str;
        /* 区别在这!*/
        timer = setInterval(fn, 1e3);
    }
    fn();
};

times.forEach((item, index) => {
    backTime(item.time, index);
});

1.3 宏任务:setInterval和setTimeout

image.png

setTimeout和setInterval的执行会等到主线程的所有任务全部执行后,才会再执行其中的回调函数,所以都是异步,但又因为它必须是等到主线程全部执行完才会执行,所以可以称之为伪异步。js异步还有一个机制,异步又分宏任务和微任务。就是遇到宏任务,先执行宏任务,将宏任务放入事件队列,执行完宏任务再将微任务放入事件队列中。

image.png

  • 宏任务:由宿主(浏览器、Node)发起的,如:setTimeout,setInterval。
  • 微任务:由Javascript自身发起的,如:Promise。
宏任务(Macrotask) 微任务(Microtask)
setTimeout、setInterval、MessageChannel、I/O,事件队列、setImmediate(Node环境) 、script(整体代码块) requestAnimationFrame(有争议)、utationObserver(浏览器环境)、Promise.[ then/catch/finally ] 、process.nextTick(Node环境)、queueMicrotask

我们了解异步都知道,异步必存在回调函数,存在回调函数不一定是异步。下面两个栗子中setTimeout和setInterval都是宏任务,而Promise是微任务,为什么会先执行Promise呢?因为setTimeout的异步在于回调函数Promise的异步回调是在then或catch或finally中,所以打印同步代码的语句属于同步任务,由于Javascript是单线程运行机制,先从同步任务开始执行,执行完再去执行异步任务,而异步任务又分宏任务和微任务。下面栗子中为什么会先执行微任务的Promise中的then呢?Javascript执行完所有同步代码,才会去执行在执行栈中的宏任务,发现宏任务中有微任务,所以先执行完所有微任务再去执行setTimeout这个宏任务,然后再到script这个宏任务script本身就是一个宏任务

  • setTimeout
setTimeout(() => {
  console.log('定时器!');
}, 0);
new Promise((resolve) => {
  console.log('同步代码!');
  resolve('异步代码!');
}).then((res) => {
  console.log(res);
});
console.log('同步代码xx!');

// 打印
同步代码!
同步代码xx!
异步代码!
定时器!
  • setInterval
setInterval(() => {
  // 无限打印
  console.log('定时器!');
}, 0);
new Promise((resolve) => {
  console.log('同步代码!');
  resolve('异步代码!');
}).then((res) => {
  console.log(res);
});
console.log('同步代码xx!');

// 打印
同步代码!
同步代码xx!
异步代码!
定时器!

2、从Rxjs上理解interval和timer

2.1 interval

  • interval定时器:基于给定时间间隔发出数字序列,返回一个发出无限自增的序列整数,可以选择固定的时间间隔进行发送。
  • 参数1:可选,默认值0,时间间隔,单位为毫秒
  • 参数2:可选,默认为async,调度器,用来提供时间的概念。
import {interval} from 'rxjs';
const interval$ = interval(3000);
interval$.subscribe((number) => {
  console.log('number', number);
});
// 打印number 1 number 2 ...

2.2 timer

timer延时器:创建一个Observable,该Observable在初始延时之后开始发送并且在每个时间周期后发出自增的数字。

  • 参数1:延时时间或日期
  • 参数2:时间周期(可选),单位毫秒
  • 参数3:调度方式,默认为async
import {interval} from 'rxjs';
const timerSub = timer(3000, 1000);
timerSub.subscribe((number) => {
  console.log('number', number);
});
// 运行三秒之后,输出0,然后间隔一秒开始输出自增数字。
//...0.1.2.3.4.5.....

🐂 举个栗子:计时器

import { take } from 'rxjs/operators';
import {interval} from 'rxjs';
interval(1000)
    .pipe(take(10))
    .subscribe({
    next: (res) => {
      console.log(res);
    },
});

3 参考资料

1、setTimeout怎么用?setTimeout与setInterval的区别

2、RxJS操作符timer、interval


不断复盘才是最好的学习,笔记只是给我们留下复习的印记,复盘才能使下一次写的更好。技术学得再好,如果不能把它说清楚,把它写明白,你就不会有更大的进步;文章写的再好,如果没有人给你提出指点,也是泯然于人的。

今天的文章Rxjs的interval和timer与Js的setInterval和setTimeout分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22885.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注