美文网首页
前端JS进阶六(异步)

前端JS进阶六(异步)

作者: EmilioWeng | 来源:发表于2018-09-03 15:01 被阅读0次

单线程

  • 单线程:只有一个线程,只能做一件事情
  • 原因:避免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单线程、异步的本质

相关文章

网友评论

      本文标题:前端JS进阶六(异步)

      本文链接:https://www.haomeiwen.com/subject/bdfbwftx.html