美文网首页
前端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