前端项目开发踩坑01————利用setTimeout模拟出类似setInterval及宏任务微任务

提示:
本文为js栏目:前端项目开发踩坑01————利用setTimeout模拟出类似setInterval 及宏任务微任务


前言

大屏项目等利用定时器固定时间刷新数据。

setInterval包裹Promise导致,Promise的回调执行时间与预期timeout不符的解决方案:利用setTimeout模拟出类似setInterval。


提示:以下是本篇文章正文内容,下面成功案例可供参考

先放解决方案。

解决方案

先放解决方案。

setTimeout(function() {
    new Promise(function(resolve) {
        resolve();
    }).then(function() {
        setTimeout(function() { },timeout)
    })
},timeout)

Vue项目:

export default {
  data() {
    return {
      timeout: null,
      canceled: false,
      callback: null,
      delay: 10000,
    }
  },
  mounted() {
    const that = this
    that.run()
  },
  created() {},
  beforeDestroy() {
    clearInterval(this.timeout)
  },
  methods: {
    run() {
      const that = this
      if (this.canceled) {
        return
      }
      that.getDataCount()
    },

    getDataCount() {
      const that = this
      getDatePromise().then(res => {
        if (res.code == '200') {
          clearInterval(that.timeout)
          that.timeout = null
          that.timeout = setTimeout(that.run, that.delay)
        } else {
          that.run()
          return
        }
      }).catch(() => {
        that.run()
      }).finally(() => {

      })
    },
    start() {
      this.canceled = false
      this.run()
    },
    cancel() {
      clearTimeout(this.timeout)
      this.timeout = null
      this.canceled = true
    }
  }
}

疑难杂症

最近干了一个大屏项目,一般来说,大屏数据的更新要么用计时器,要么用长连接,websocket,signal这些对吧。

然后根据领导的要求,数据的更新用的计时器。活爹,用就用呗。

然后就遇到问题了,一开始的想法是使用setInterval()中固定时间调用接口,后来发现不行。

setTimeout、setInterval和Promise

大多数项目中,我一般封装request.js时,会选择把响应结果封装成Promise 。在这个项目中一开始没有考虑到setInterval和Promise 执行机制的问题。

JavaScript中定时器主要有setTimeout和setInterval,但是它们在执行时往往和我们设置的延迟时间有出入,这点大家都懂。简单解释一下。

首先记住:

JavaScript始终是同步的并且是单线程的。

JavaScript 在浏览器环境中主要是单线程的,在某些情况下(如 Node.js 或 Web Workers)可以表现出多线程的特性。然而,JavaScript 的设计哲学和主要执行模型是基于单线程的。

JavaScript 通过异步编程模型来处理耗时的操作,如网络请求、定时器、DOM 事件等。这些操作被划分为宏任务。

这涉及到微任务和宏任务,

微任务和宏任务

首先代码可以划分为同步代码和异步代码,异步代码又被划分为宏任务和微任务,微任务与宏任务分属不同的任务队列(Task Queue)。

先执行同步代码,后执行异步代码,执行异步代码时,先执行微任务队列中的微任务。而宏任务不会阻塞主线程的执行,而是将它们放入任务队列中等待。当主线程空闲时,事件循环会从任务队列中取出任务并执行它们。

同步->微任务->宏任务,循环往复。

微任务和宏任务最大的不同在于,如果在执行微任务的过程中我们往任务队列中新增了任务,浏览器会全部消费掉为止,再进入下一个循环。

这使得 JavaScript 能够在不阻塞主线程的情况下处理异步操作。

setTimeout和setInterval都属于宏任务(MacroTask/Task),Promise 属于微任务(MicroTask),且Promise在实例化的过程中所执行的代码都是同步进行的。

如果遇到这种情况,宏任务内包裹微任务

setInterval(()=>{
    //code1..
    new Promise(resolve =>{
        resolve();
    }).then(()=>{
        // code2..
    });
},timeout);

遇到setInterval,异步宏任务,将code1..放入宏任务队列中;

new Promise在实例化的过程中所执行的代码都是同步进行的,但是.then中注册的回调是异步执行的,所以将其放入微任务队列中。

而计时器在timeout时间后生成的又是宏任务,只是将事件放消息队列,真正执行的时间并不确定,有可能上一个计时器任务没执行完又进来一个计时器任务,所以会造成.then内执行时间与预期执行时间不同,这就造成了奇奇怪怪的bug。

事件循环Event Loop

都讲到这了,拓展一下什么叫事件循环。

JavaScript的执行机制是居于事件循环的。

再次重申,**JavaScript始终是同步的并且是单线程的。**同一时间,浏览器只能执行一个任务。

为了避免某些长时间任务造成无意义等待,JavaScript 引入了异步任务队列,

如图:
在这里插入图片描述

同步任务放在执行栈中执行,异步任务由Web Apis处理放到任务队列中,执行栈中的任务执行完毕会直接去查询任务队列中是否有异步任务,如果宏任务和微任务队列不为空,则以微任务->宏任务的过程执行。由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,循环往复,所以叫事件循环,我是真讨厌循环。

宏任务内包裹微任务的形式中,

setInterval(()=>{
    //code1..
    new Promise(resolve =>{
        resolve();
    }).then(()=>{
        // code2..
    });
},timeout);

主线程从宏任务队列中获取到setInterval,执行后生成一个微任务,加入微任务队列,但没结束,又把setInterval加入宏任务队列,当timeout时间比较短的时候,就出问题了,可能上一条Promise没执行结束,下一条setInterval已经生成了新的微任务,这就造成顺序混乱了。

  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值