1.5.3 Promise和async/await

1.Promise

Promise是一种适用于异步操作的机制,比传统的回调函数解决方案更合理和更强大。从语法上说,Promise是一个对象,从它可以获取异步操作的结果:成功或失败。在Promise中,有三种状态:pending(进行中)、resolved(已成功)和rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,无法被Promise之外的方式改变。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。创建一个Promise对象,代码如下:

     var promise = new Promise(function(resolve, reject) {
       ...
       if (/* 异步操作成功 */){
         resolve(value);
       } else {
         reject(error);
       }
     });

在上面的代码中,创建了一个Promise对象,Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。这是两个内置函数,resolve函数的作用是将Promise对象的状态变为“成功”,在异步操作成功时调用,并将异步操作的结果作为参数传递出去;reject函数的作用是将Promise对象的状态变为“失败”,在异步操作失败时调用,并将异步操作报出的错误作为参数传递出去。当代码中出现错误(Error)时,就会调用catch回调方法,并将错误信息作为参数传递出去。

Promise对象实例生成后,可以用then方法分别指定resolved(成功)状态和rejected(失败)状态的回调函数以及catch方法,比如:

     promise.then(function(value) {
       // success逻辑
     }, function(error) {
       // failure逻辑
     }).catch(function(){
       // error逻辑
     });

then()方法返回的是一个新的Promise实例(不是原来那个Promise实例)。因此,可以采用链式写法,即then()方法后面再调用另一个then()方法,比如:

     getJSON("/1.json").then(function(post) {
       return getJSON(post.nextUrl);
     }).then(function (data) {
       console.log("resolved: ", data);
     }, function (err){
       console.log("rejected: ", err);
     });

下面是一个用Promise对象实现的Ajax操作get方法的例子。

     var getJSON = function(url) {
       // 返回一个Promise对象
       var promise = new Promise(function(resolve, reject){
         var client = new XMLHttpRequest(); //创建XMLHttpRequest对象
         client.open("GET", url);
         client.onreadystatechange = onreadystatechange;
         client.responseType = "json";              //设置返回格式为json
         client.setRequestHeader("Accept", "application/json");//设置发送格式为json
         client.send();//发送
         function onreadystatechange() {
           if (this.readyState !== 4) {
             return;
           }
           if (this.status === 200) {
             resolve(this.response);
           } else {
             reject(new Error(this.statusText));
           }
         };
       });
       return promise;
     };
     
     getJSON("/data.json").then(function(data) {
       console.log(data);
     }, function(error) {
       console.error(error);
     });

了解Promise的基本知识可以便于后续学习使用服务端渲染。当然,Promise的应用场合还是比较多的,如果想要深入了解,可以访问网址:https://developer.mozilla.org/en-US/docs/Web/Java Script/Reference/Global_Objects/Promise,进行系统的学习。

2.async/await

async/await语法在2016年就已经提出来了,属于ES 7中的一个测试标准(目前来看是直接跳过ES 7,列为ES 8的标准了),它主要为了解决下面两个问题:

· 过多的嵌套回调问题。

· 以Promise为主的链式回调问题。

前面讲解过Promise,虽然Promise解决了恐怖的嵌套回调问题,但是解决得并不彻底,过多地使用Promise会引发以then为主的复杂链式调用问题,同样会让代码阅读起来不那么顺畅,而async/await就是它们的救星。

async/await是两个关键字,主要用于解决异步问题,其中async关键字代表后面的函数中有异步操作,await关键字表示等待一个异步方法执行完成。这两个关键字需要结合使用。

当函数中有异步操作时,可以在声明时在其前面加一个关键字async,代码如下:

     async function myFunc() {
       //异步操作
     }

使用async声明的函数在被调用时会将返回值转换成一个Promise对象,因此async函数通过return返回的值会进入Promise的resolved状态,成为then方法中回调函数的参数,代码如下:

     // myFunc()返回一个Promise对象
     async function myFunc() {
       return 'hello';
     }
     // 使用then方法就可以接收到返回值
     myFunc().then(value => {
       console.log(value); // hello
     })

如果不想使用Promise的方式接收myFunc()的返回值,可以使用await关键字更加简洁地获取返回值,代码如下:

     async function myFunc() {
       return 'hello';
     }
     let foo = await myFunc(); // hello

await表示等待一个Promise返回,但是await后面的Promise对象不会总是返回resolved状态,如果发生异常,则进入rejected状态,那么整个async异步函数就会中断执行,为了记录错误的位置和编写异常逻辑的代码,需要使用try/catch,代码如下:

     try {
       let foo = await myFunc(); // hello
     }catch(e){
       // 错误逻辑
       console.log(e)
     }

下面举一个例子,在后面的实战项目开发中,经常会用到数据接口请求数据,接口请求一般是异步操作,例如在Vue的mounted方法中请求数据,代码如下:

     async mounted () {
         // 代码编写自上而下,一行一行,以便于阅读
       let resp = await ajax.get('weibo/list')
       let top = resp[0]
       console.log(top)
     }

在上面的代码中,ajax.get()方法会返回一个Promise,采用await进行了接收,并且await必须包含在一个用async声明的函数中。

可以看出,在使用了async/await之后,整个代码的逻辑更加清晰,没有了复杂的回调和烦琐的换行。

至此,对于实战项目中用到的相关ES 6语法基本讲解完毕,如果读者想进一步了解ES 6的更多语法知识,可以自行在其官网上学习。