Javascript 异步编程(三)定时器

Javascript 异步编程(三)

并行?并发?异步?

同步:synchronous: 指所有任务按出现的先后顺序依次执行 如果出现阻塞的任务,那么线程就会等待这个任务完成,接着执行下一个任务。

异步:asynchronous:不保证所有任务按出现的顺序执行

并发:concurrent:从宏观上,某个时间段里面多个程序都得到了运行,但不是说“同时运行”

并行:parallel:在多核心下,因进程和线程独立运行,且多个线程之间共享数据,程序可以同时运行。

定时器

常用的回调函数有:

  • setTimeout
  • setInterval
  • setImmediate(Node.js)
  • requestAnimationFrame

https://zhuanlan.zhihu.com/p/55129100

setTimeout

作用:延迟指定的时间来调用函数或计算表达式。

语法:setTimeout(func /**函数,必选*/,code /**表达式,可选*/ ,milliseconds /**执行需等待的毫秒数,必选*/param1, param2, .../**传递给函数的参数,可选*/)

具体用法参加《Javascript异步编程(一)》

setInterval

作用:按照指定的周期(以毫秒计)来调用函数或计算表达式。

语法:setInterVal(func /**函数,必选*/,code /**表达式,可选*/ ,milliseconds /**每次执行将延迟的毫秒数,必选*/param1, param2, .../**传递给函数的参数,可选*/)

原理

 console.log('sync...',1);
  setInterval(()=>{
    console.log('sync...',2)
  },2000);
  console.log('sync...',3)

流程分析:

  1. 主程序调用栈
  2. 调用webAPI-setInterVal
  3. 定时器线程计数2s
  4. 每隔2s事件触发线程将回调放入任务队列
  5. 主线程通过Event Loop遍历任务队列执行回调 console.log('sync...',2)

注意

  • IE9及以下版本不兼容(setTimeout&setInterval)传递额外参数,可使用polyfill
  • setInterval,delay最小为10ms。意味着无法设置为0秒间隔,但可以尝试使用postMessage实现
  • 使用clearInterval(timerId)关闭指定定时器
  • 确保回调函数执行时长小于delay时间
  • 同setTimeout一样,setInterval回调中的this永远指向global
  • 不推荐使用setInterval(code,delay),有安全风险

为什么不建议使用setInterval

如果任务实际耗时超过delay,会出现同一时间触发多个回调。

有以下场景,每隔1s调用服务

// 时间间隔大于delay
let count = 5
let intervalTimer = setInterval(function () {
  if (count <= 0) {
    clearInterval(intervalTimer)
    return
  }
  /*模拟延时任务*/
  let timeoutTimer = setTimeout(function (count) {
    console.log(`the ${count} is running`)
    clearTimeout(timeoutTimer)
  }, Math.floor(Math.random() * (10000) + 1000),count)
  count--
}, 1000)
image_9.png

从上图可知,xhr响应无法按照顺序返回,这样就会导致无法正常处理结果

折中方案

function moreBetterInterval (count) {
  // 1s后调用
  setTimeout(function (countDown) {
    console.log(count +' is begin')
    let timeoutTimer = setTimeout(function (times) {
      console.log(`the ${times} is running`)
      clearTimeout(timeoutTimer)
      times--;
      if(times>0){
        moreBetterInterval(times)
      }
    }, Math.floor(Math.random() * (10000) + 1000),countDown)
  }, 1000,count)
}
moreBetterInterval(5)
image_10.png

可以保证递归之前已执行完回调,但无法保证按照一定的时间间隔。

实际应用场景

  • 短信倒计时
let countDown=60;
let timer=setInterval(function () {
  countDown--;
  if(countDown<0){
    clearInterval(timer);
    countDown=60
  }
},1000)
  • 显示当前时间
setInterval(function () {
    let d = new Date()
    $('#clock')[0].innerHTML = d.toLocaleTimeString()
}, 1000)

setImmediate

仅在Internet Explorer和Node.js下可用

作用:在循环事件任务完成后马上运行指定代码

语法:setImmediate(func /**函数,必选*/,code /**表达式,可选*/,[ param1,param2,...]/**传递给函数的参数,可选*/);

setImmediate与setTimeout(func,0)?

setTimeout(func,0)

需要考虑

  • 是在哪个阶段注册的,如果在定时器回调或者I/O回调里面,setImmediate肯定先执行。如果在最外层或者setImmediate回调里面,哪个先执行取决于当时机器状况。
  • 当前的Event Loop的任务队列的情况,如果在队尾,那也要先执行前面的事件,这样也无法保证立即执行

requestAnimationFrame

作用:接受一个动画执行函数作为参数,这个函数的作用是仅执行一帧动画的渲染,并根据条件判断是否结束,如果动画没有结束,则继续调用requestAnimationFrame并将自身作为参数传入

语法:requestAnimationFrame(func /**函数,必选*/)

细节:以60FPS(每帧16.7ms)为目标,浏览器内部会选择渲染的最佳时机

与setTimeout动画区别:

  • setTimeout(func,16.7):容易卡顿

原因有两个:

  • 实际执行时间晚于设定的延迟时间,出现卡顿
  • 与浏览器刷新率有关,不同设备的屏幕刷新频率可能会不同,而setTimeout只能设定固定的时间间隔,无法保证与刷新率同步,容易丢帧
  • requestAnimationFrame能节省CPU开销,当元素隐藏或不可见时,会停止渲染。而setTimeout仍在后台执行。

用途

  • 实现一帧的函数节流
  • 动画

注意

  • 使用cancelAnimationFrame(id)关闭渲染动画
  • IE10以下不兼容,可使用setTimeout进行polyfill 从而模拟帧率尽量适配刷新率

结尾

通过以上定时器,最显著的共同点是:回调。

初一看,回调没有问题呀,可以延迟计算。请想下以下情景:

  1. 嵌套回调
let msg = document.getElementById('msg')
$('#btn').click(function (evt) {
  msg.innerHTML += `${new Date()} processing btn click callback... <br>`
  setTimeout(function request () {
    msg.innerHTML += `${new Date()} processing setTimeout callback...<br>`
    $.get('https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js', function (data, status) {
      msg.innerHTML += `${new Date()} processing ajax callback...<br>`
    })
  }, 500)
}) 

这里我们用了三个函数嵌套,这种代码就被称为“回调地狱(callback hell)”,这样的代码难以编写,难以理解而且难以维护

  1. 控制反转
$.get('https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js', function (data, status) {
  msg.innerHTML += `${new Date()} processing ajax callback...<br>`
})

即自己程序的一部分的执行控制权交由某个第三方。在你不确定这个回调能否按照预期执行时,发生意外时很难定位问题。

为了优雅的的处理回调最大的问题:控制反转,有以下方式:

  • 回调分离 -->ES6 Promise
  • error-first风格-->Node.js中会将回调的第一个参数保留用作error
const fs=require('fs')
fs.readFile(__dirname,function(err,data) {
  if (err)  console.log(err)
  //...
})

但还是不优雅,并没有真正解决我们的控制反转问题,只是将我们之前担心的程序异常暴露了出来。

可能现在你希望有API或其他语言机制来解决这些问题。所幸,ES6会给你带来些干货~

Reference

HTML Timers
Timer resolution in browsers

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348

推荐阅读更多精彩内容