前端JS进阶六(异步)

单线程

  • 单线程:只有一个线程,只能做一件事情
  • 原因:避免DOM渲染的冲突
  • 解决方案:异步
// 循环运行期间,JS执行和DOM渲染暂时卡顿
  var i,sum = 0;
  for(i=0;i<10000000;i++){
    sum += i;
  }
  console.log(sum);
// alert不处理,JS执行和DOM渲染暂时卡顿
  console.log(1)
  alert('hello')
  console.log(2)

避免DOM渲染冲突

  • 浏览器需要渲染DOM
  • JS可以修改DOM结构
  • JS执行的时候,浏览器DOM渲染会暂停
  • 两段JS也不能同时执行(防止DOM渲染冲突)
  console.log(100)
  setTimeout(function(){
    console.log(200)   //1000ms之后执行
  },1000)              //1000ms后再进入异步队列,先让其他JS代码执行
  console.log(300)
  console.log(400)
// 100 300 400 200

  console.log(100)
  $.ajax({
    url:'xxxx',
    sucess:function(result){  //ajax加载完才执行
      console.log(result)     //ajax后再进入异步队列,先让其他JS代码执行
    }
  })
  console.log(300)
  console.log(400)
// 100 300 400 result

异步存在的问题

  • 没按照书写方式执行,可读性差
  • callback中不容易模块化(callback就是异步后需要执行的函数)

事件轮询 event-loop

  • 事件轮询是JS实现异步的具体解决方案
  • 同步代码,直接执行
  • 异步函数先放在异步队列中
  • 待同步函数执行完毕,轮询执行异步队列
  setTimeout(function(){
    console.log(1)
  },)
  setTimeout(function(){
    console.log(2)
  },100)
  console.log(3)

//主进程
  console,log(3)

//异步队列
  //立即被放入异步队列
  function(){  
    console.log(1)
  })
  //100ms后这个函数会被放入异步队列
  function(){  
    console.log(2)
  })

jQuery-deferred

  • jQuery1.5版本后引入jQuery-deferred
  • 初步引入Promise的概念
//jQuery1.5之前
  var ajax = $.ajax({
    url:'data.json',
    success:function(){
      console.log('success1')
      console.log('success2')
      console.log('success3')
    },
    error:function(){
      console.log('error')
    }
  })
  console.log(ajax) // 返回一个XHR对象

//jQuery1.5之后
  //1 .done.fail写法
  var ajax = $.ajax('data,json')
  ajax.done(function(){
    console,log('success1')
  }).fail(function(){
    console,log('error')
  }).done(function(){
    console,log('success2')
  })
  console.log(ajax) // 返回一个deferred对象

  //2 .then写法 很像Promise的写法
  var ajax = $.ajax('data.json')
  ajax.then(function(){
    console.log('success1')
  },function(){
    console.log('error1')
  }
  .then(function(){
    console.log('success2')
  },function(){
    console.log('error2')
  }

jQuery1.5之后带来的变化

  • 无法改变JS异步和单线程的本质
  • 只能从写法上杜绝callback这种形式
  • 它是一种语法糖形式,只是解耦了代码
  • 很好的体现了开放封闭的原则

使用jQuery Deferred

//给出一段简单异步代码,使用setTimeout函数
  var wait = function(){
    var task = function(){
      console.log('执行完成')
    }
    setTimeout(task,2000)
  }
  wait()

//使用jQuery Deferred
  function waitHandle(){
    var dtd = $.Deferred() //创建一个deferred对象

    var wait = function(dtd){ //要求传入一个deferred对象
      var task = function(){
        console.log('执行完成')
        dtd.resolve() //表示异步队列已经完成
        //dtd.reject()  //表示异步任务失败或者出错
      }
      setTimeout(task,1000)
      return dtd  //要求返回deferred对象
    }
    //注意这里一定要有返回值
    return wait(dtd)
    }

  var w = waitHandle()
  w.then(function(){
    console.log('ok 1')
  },function(){
    console.log('err 1')
  })
  w.then(function(){
    console.log('ok 2')
  },function(){
    console.log('err 2')
  })
  // 也可以使用 w.done w.fail
  • deferred对象dtd的API分为两类
    第一类:dtd.resolve dtd.reject (主动去执行异步函数结果 主动触发的)
    第二类:dtd.then dtd.done dtd.fail(被动监听异步函数结果 被动监听的)
    这两类不能混用,否则后果比较严重

使用dtd.promise()

  function waitHandle(){
    var dtd = $.Deferred()
    var wait = function(dtd){
      var task = function(){
        console.log('执行完成')
        dtd.resolve()
        //dtd.reject()
      }
      setTimeout(task,1000)
      return dtd.promise()  //注意 这里返回promise而不是返回deferred对象
    }
    return wait(dtd)
    }

 var w = waitHandle() //经过上面的修改 这里的w是promise对象
  $.when(w)
  .then(function(){
    console.log('ok 1')
  })
  .then(function(){
    console.log('ok 2')
  })
  //注意 这里执行w.reject()会报错
  //函数内部封装的时候可以使用resolve reject 函数外部使用时只能用then done fail被动监听了

总结

  • jQuery1.5对ajax的改变
  • 举例说明如何简单的封装和使用Deferred(强调开放封闭原则)
  • Deferred和Promise的区别
    Deferred有resolve、reject这种主动触发的函数,也有then、done、fail这种被动监听的函数,容易混用。
    Promise只能使用then、done、fail被动监听,不能主动修改。

Promise

promise语法见之前的文章:https://www.jianshu.com/p/54c7c55677f7

  function loadImg(src){
      const promise = new Promise (function (resolve,reject){
          var img = document.createElement('img')
          img.onload = function(){
              resolve(img)
          }
          img.onerror = function(){
              reject()
          }
          img.src = src
      })
      return promise
  }

  var src = 'https://www.imooc.com/static/img/index/logo_new.png'
  var result = loadImg(src)
  result.then(function(img){
      console.log(img.width)
  },function(){
      console.log('failed')
  })
  result.then(function(img){
      console.log(img.height)
  },function(){
      console.log('failed')
  })

Promise异常捕获

catch不仅能捕获程序逻辑语法的报错,还能捕获reject()返回的报错

//规定then只接收一个成功的参数,最后统一用catch捕获异常
  result.then(function (img){
    console.log(img.width)
  }).then(function (img){
    console.log(img.height)
  }).catch(function (ex){
    console.log(ex)  //统一用catch捕获异常
  })

Promise多个串联

我们要求程序的图片按顺序加载,先加载图片1,再加载图片2

  var src1 = 'https://www.baidu.com/static/img/index/logo1.png'
  var result1 = loadImg(src1)
  var src2 = 'https://www.baidu.com/static/img/index/logo2.png'
  var result2 = loadImg(src2)
  //链式操作
  result1.then(function (img){
    console.log('图片1加载完毕')
    return result2  //一定要return result2!!! 非常重要!
  }).then(function (img){
    console.log('图片2加载完毕')
  }).catch(function (ex){
    console.log(ex)  //统一用catch捕获异常
  })

Promise.all和Promise.race

//Promise.all接收一个Promise对象的数组
  //待全部完成之后,统一执行success
  Promise.all([result1,result2]).then(datas=>{
    //接收到的datas是一个数组,依次包含了多个promise返回的内容
    console.log(datas[0])
    console.log(datas[1])
  }) 

//Promise.race接收一个Promise对象的数组
  //只要有一个完成,就执行success
  Promise.race([result1,result2]).then(datas=>{
    //接收到的data即最先完成的promise的返回值
    console.log(datas[data])
  }) 

Promise状态

  • 三种状态:pending、fulfilled、rejected
  • 初始转态是pending
  • pending变成fulfilled,或者pending变成rejected
  • 转态不可逆

Promise的then

  • Promise实例必须实现then这个方法
  • then()必须接受两个函数作为参数
  • then()返回的必须是一个Promise实例

async/await (ES7中的标准)

  • then只是将callback函数拆分了
  • async/await是最直接的同步写法
  const load = async function(){
    const result1 = await loadImg(src1)
    console.log(result1)
    const result2 = await loadImg(src2)
    console.log(result2)
  }
  load()

用法

  • 使用await,函数必须使用async标识
  • await后面跟的是一个Promise实例
  • 需要babel-polyfill

特点

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

推荐阅读更多精彩内容

  • 1,什么是单线程,和异步有什么关系 单线程-只有一个线程,只能做一件事 单线程的原因:避免DOM 渲染的冲突 浏览...
    darrell阅读 562评论 0 1
  • title: promise总结 总结在前 前言 下文类似 Promise#then、Promise#resolv...
    JyLie阅读 12,228评论 1 21
  • Promise 的含义 一句话概括一下promise的作用:可以将异步操作以同步操作的流程表达出来,避免了层层嵌套...
    雪萌萌萌阅读 5,459评论 0 7
  • 弄懂js异步 讲异步之前,我们必须掌握一个基础知识-event-loop。 我们知道JavaScript的一大特点...
    DCbryant阅读 2,707评论 0 5
  • 风和日丽下的清凉山,更显得天蓝、桃红、庄稼喜人。 我这个被乡党称为“要不够”的贫困户老朱,近来也显得有了精神,...
    邓文伟阅读 402评论 0 0