zoukankan      html  css  js  c++  java
  • ES6语法(3)—— 用promise()对象优雅的解决异步操作

    Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。

      所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

      上面两段话是阮大神对于promise对象的一些总结,我再浓缩一下

      Promise就是用于处理异步操作的!
       关于异步操作和同步操作的区别以及他们在浏览器中的表现形式,可以转至这篇文章了解一下

      为什么要处理异步操作?
      需求1:
      在你登录一个网站的时候,这个网站的所有信息都依赖于当前用户的id才能获取,而要得知用户的id,取决于登录接口的返回值。因此,只有当获取用户信息的接口访问成功后,你才能拿到id,才能继续执行别的ajax。(当然实际情况下,id值如此重要的情况下应该保存在session或者cookie中用于前后端交互)

      由于异步操作的特殊性,大多数情况下,你还没有拿到用户id,其他的接口请求就带着id为undefined的请求头发出去了,这样就会导致你拿不到其他接口的数据,打开network,可以看到请求头里的user_id都是undefined。

      如果要避免这种情况的发生,ajax提供了是否需要对当前接口进行同步操作处理的参数,默认情况下资源请求都是异步的。这是一种方法,当然不可避免地,这个接口会阻塞当前进程,使得其他接口在这个接口状态变成success之前都处于阻塞状态。

      ES6提供了更加优雅的解决方案 --- promise()
      在解决上述问题之前,先来介绍下promise的一些用法和常识。

      Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,这两个参数都是函数,如下所示

    new Promise(function(resolve,reject){
      异步操作 ajax 定时器等{
        if(结果满意)
          resolve(value)
        else
          reject(error)
      }
    })

      Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。如下所示

    function timeout(ms) {
      return new Promise((resolve, reject) => {
        if(ms>=100)
         setTimeout(resolve, ms, 'done'); //setTimeout的第三个参数用于给第一个参数(函数)传参
        else
         setTimeout(reject, ms, 'error'); //setTimeout的第三个参数用于给第一个参数(函数)传参
      });
    }
    timeout(100).then((value) => {
      console.log(value);  //done
    },(error)=>{
      console.log(error)
    });
    timeout(100).then((value) => {
      console.log(value);
    },(error)=>{
      console.log(error)  //error
    });

      promise实例上除了then(),还有catch和finally方法。

      catch方法用于指定发生错误时的回调,也就是.then(null,function()),稍微修改下上面的代码

    timeout(100).then((value) => {
      console.log(value);  
    }.catch((error)=>{
      console.log(error)
    });

      当然作者不会那么闲的蛋疼,为了这点小事搞个catch出来,catch还有个功能他可以检测resolve中抛出的异常,也就是说如果resolve中发生异常,JS不会报错,而会把错误抛给catch的第一个参数,如果你写了的话。如下

    timeout(100).then((value) => {
      console.log(value);
      console.log(somethingUndefined)
    }).catch((error)=>{
      console.log(error)  //somethingUndefined is not defined
    });

      finally()方法不能传入任何参数,他可以在当前promise状态改变后执行,至于前面的异步操作是否成功执行,跟他无关,也就是说,finally只跟当前异步操作是否执行完毕有关,比如我想在某个异步操作之后,执行一个动画,那么动画跟数据是否传输成功并没有什么关系,不管是成功还是NaA还是undefined,我都可以愉快的执行动画。

      简单了解了promise后,大概可以解决需求1中的问题了,来看一场4*100接力赛吧

    // 4 * 100 接力赛

    let [man1,man2,man3,man4] = [{v:5,rank:1},{v:8,rank:2},{v:10,rank:3},{v:4,rank:4}]
    let runningRace = (man) => {
        return new Promise((resolve,reject) => {
            // setTimeout的计算单位是毫秒,乘以100效果好一些
            setTimeout(resolve,100/man['v']*100,'rank'+ man['rank'] +'跑了' + 100/man['v'] + 's')
        })
    }
    runningRace(man1).then((info)=>{
        console.log(info)
        return runningRace(man2)
    }).then((info)=>{
        console.log(info)
        return runningRace(man3)
    }).then((info)=>{
        console.log(info)
        return runningRace(man4)
    }).then((info)=>{
        console.log(info)
    })

      上面这个接力的例子,已经完美的解决了异步操作中的先后执行问题。同时,链式操作的方式使得代码的易读性更强,你无需在回调的嵌套中一步步走入深渊。

      需求2:
       如果一个接口的访问时间超过400毫秒就算超时,如何解决。

       传统的解决思路(在面试的时候想到的),设个定时器,设个全局变量data接受接口要返回的数据,如果400毫秒后,定时器的全局data还未被赋值,说明接口请求超时,当然这种做法不够优雅,如果用promise()呢?

    // 接口超时模拟
    function ajaxTimeOut(){
      return new Promise(function(resolve,reject){
        let data = null
        // ajax操作用setTimeout模拟
        setTimeout(function(){
          data = '真实数据来了'
        },388)
        setTimeout(function(){
          if(data === null){
            reject('接口访问超时')
          } else {
            resolve(data)
          }
        },400)
      })
    }
    ajaxTimeOut().then(function(data){
      console.log('success,data is' + data)
    }).catch(function(err){
      console.log('err信息:'+err)
    })

      看起来好像解决了,但是怎么看怎么像愚蠢的解决方案嵌套了一层美丽的皮囊。

      promise下还有两个重要的方法没有提到,分别是all()和race()

      Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

    const p = Promise.all([p1, p2, p3]);

      上面代码中,Promise.all方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。

    p的状态由p1、p2、p3决定,分成两种情况。

    (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

    (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

      现在我们来改造一下刚才的4*100接力赛,下面进行的是100米的个人赛,我们将在比赛结束后统计结果

    Promise.all([runningRace(man1),runningRace(man2),runningRace(man3),runningRace(man4)])
    .then(function(results){
    console.log(results)
    })

      可以看到,在比赛结束后(用时最长的异步加载成功后),统计的结果以数组的形式放在第一个参数中。

      Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

    const p = Promise.race([p1, p2, p3]);

    上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

    Promise.race方法的参数与Promise.all方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。

      说这么多,反正race的意思就是比赛,就是比哪个异步操作的速度快,改造一下跑步的代码

    // 跑步比赛
    Promise.race([runningRace(man1),runningRace(man2),runningRace(man3),runningRace(man4)])
    .then(function(result){
    console.log(result) //谁跑得快输出谁,别的人都忽略
    })

    上述的结果只有一个,那就是,第一名!

     ok,下面来解决下超时的需求,代码如下。

    // 接口超时模拟2

    // 接口超时模拟2
    function ajax(){
      return new Promise(function(resolve,reject){
        setTimeout(function(){
          resolve('success')
        },2000)
      })
    }
    function timeOut(){
      return new Promise(function(resolve,reject){
        setTimeout(function(){
          reject('超时')
        },400)
      })
    }
    Promise.race([ajax(),timeOut()])
    .then(function(data){
      console.log(data)
    })
    .catch(function(err){
      console.log(err)
    })


     

  • 相关阅读:
    网页加速的14条优化法则 网站开发与优化
    .NET在后置代码中输入JS提示语句(背景不会变白)
    C语言变量声明内存分配
    SQL Server Hosting Toolkit
    An established connection was aborted by the software in your host machine
    C语言程序设计 2009春季考试时间和地点
    C语言程序设计 函数递归调用示例
    让.Net 程序脱离.net framework框架运行
    C语言程序设计 答疑安排(2009春季 110周) 有变动
    软件测试技术,软件项目管理 实验时间安排 2009春季
  • 原文地址:https://www.cnblogs.com/xtjatswc/p/10325104.html
Copyright © 2011-2022 走看看