零基础学透 TypeScript 之30 Promise及其语法糖async和await

        TS 在 1.6 版本实验性地支持了 async 函数。在过去的 JavaScript 当中,如果想保证代码的执行顺序,需要使用回调函数,当需要执行的步骤多了时就会陷入当说的“回调地狱”。自从 ES6 增加了 Promise 之后,状况有了缓解,我们先来看个例子,一个简单的多个 ajax 请求的例子:

ajax.post( // 这里你可以先忽略ajax的定义,他的post方法用来发送一个post请求
  "/login", // 第一个参数时要请求的url
  {
    data: {
      username: "lison",
      password: "xxxxx"
    }
  }, // 第二个参数是这个请求要携带的参数
  function(res) {
    var userid = res.data.userid;
    ajax.post( // 这里在/login接口成功返回数据后,再调用一个/userroles接口,用来获取该登录用户的角色信息
      "/userroles",
      {
        data: {
          userid: user_id
        }
      },
      function(res) {
        var role = res.data.role;
        console.log(role);
      }
    );
  } // 第三个参数是接口响应之后的回调函数
);

        在这个例子中,我们先调用登录的接口发送用户名和密码,然后服务端进行校验之后返回这个用户的一些信息,然后我们可以从信息中拿到用户 id 去获取它的角色用于权限控制。这个过程是有先后顺序的,必须先登录后获取角色,为了保证这个顺序,在过去要使用回调函数,当然一些库也支持链式调用。再来看下使用 ES6 的 Promise 需要怎么写:

const loginReq = ({ username, password }) => { // 封装一个loginReq函数,用来返回一个Promise,用来调用/login接口
  return new Promise((resolve, reject) => { // Promise接收一个回调函数参数,这个函数有两个参数,两个参数都是回调函数
    ajax.post(
      "/login",
      {
        username,
        password
      },
      res => {
        resolve(res); // 第一个参数resolve用来在执行成功后调用,传给他的参数,可以在这个promise的then函数参数中获取到
      },
      error => {
        reject(error); // 第二个参数reject用来在执行出现错误后调用,传给他的错误信息,可以在这个promise的catch函数参数中获取到
      }
    );
  });
};
const getRolesReq = ({ userid }) => { // 封装一个getRolesReq函数,用来返回一个Promise,用来调用/userroles接口
  return new Promise((resolve, reject) => {
    ajax.post(
      "/userroles",
      {
        data: {
          userid
        }
      },
      res => {
        resolve(res);
      },
      error => {
        reject(error);
      }
    );
  });
};
loginReq({ username: "lison", password: "xxxxx" }).then(res => { // 这里在调用loginReq函数后返回一个Promise,在内部当执行到resolve的地方时,这里的then的回调函数就会执行
  getRolesReq({ userid: res.data.user_id }).then(res => {
    console.log(res.data.role);
  });
});

        这看起来代码变长了,但是当我们搭配使用诸如 Axios 这类 ajax 请求库和 ES6 语法时,对于一些复用性高的接口调用能够起到很好的封装作用,而且使用起来较为简洁。

        ES7 中增加了 async 和 await 的规范,它们其实是 Promise 的语法糖。TypeScript 在 1.6 支持了 async 和 await,下面我们通过 setTimeout 来实现异步过程,看下在 TypeScript 中如何使用 async 和 await:

interface Res { // 我们定义一个接口,用来定义接口返回结果的结构
  data: {
    [key: string]: any;
  };
}
namespace axios { // 现在我们来定义一个命名空间,用来模拟axios实现接口调用
  export function post(url: string, config: object): Promise<Res> { // 返回值类型是一个Promise,resolve传的参数的类型是Res
    return new Promise((resolve, reject) => { // 然后这里返回一个Promise
      setTimeout(() => { // 通过setTimeout实现异步效果
        let res: Res = { data: {} };
        if (url === "/login") res.data.userid = 111; // 我们这里通过简单判断,来模拟调用不同接口返回不同数据的效果
        else res.data.role = "admin";
        console.log(2);
        resolve(res); // 在这里传入res结果
      }, 1000);
    });
  }
}
interface Info {
  username: string;
  password: string;
}
async function loginReq({ username, password }: Info) { // 这里使用async关键字修饰这个函数,那么他内部就可以包含异步逻辑了
  try {
    console.log(1);
    const res = await axios.post("/login", { // 这里调用/login接口
      data: {
        username,
        password
      }
    });
    console.log(3);
    return res;
  } catch (error) {
    throw new Error(error);
  }
}
async function getRoleReq(userid: number) {
  try {
    const res = await axios.post("/userroles", {
      data: {
        userid
      }
    });
    return res;
  } catch (error) {
    throw new Error(error);
  }
}
loginReq({ username: "lison", password: "123" }).then(res => {
  const {
    data: { userid }
  } = res;
  getRoleReq(userid).then(res => {
    const {
      data: { role }
    } = res;
    console.log(role);
  });
});

        这个例子中用到了很多我们前面学习到的知识,可以帮大家进行复习和实践。

        首先我们定义一个命名空间 axios,定义它用来简单模拟 axios 这个 ajax 请求库。我们在命名空间内定义一个 post 方法,第一个参数是要请求的 url,第二个参数是一些配置,这里我们只是定义一个参数,不做具体参数的处理和判断。这个 post 方法返回一个 Promise,这和 axios 库是一样的,使用 setTimeout 来模拟 ajax 请求的异步行为和延迟,当 1 秒后调用 resolve 回调函数,并根据调用的 url 返回不同的结果。post 方法返回的是一个 Promise,所以这个函数返回类型我们使用 TypeScript 内置的条件类型Promise<T>来指定返回类型,这个 T 的类型就是在 resolve 回调函数中返回的值的类型,我们这里返回的值为一个包含 data 属性的对象,所以我们定义一个接口 Res。

        接下来要定义两个发起 ajax 请求的函数了,这里我们使用 async/await 来定义这两个函数。先来看 loginReq 函数。我们在 function 关键字前加async表明这是一个异步函数,然后就可以在它的函数体内使用await关键字来让异步代码同步执行了。如果不使用 await,我们可以通过.then()拿到 axios.post 的结果并且进行后面的操作。我们这里在 axios.post 方法调用前面加上await,这样就可以让这个异步函数同步返回结果,它的返回值就是 Promise 中 resolve 回调函数传入的实际参数。我们在使用 Promise 时,可以使用.catch(error => {})来捕获异常拿到错误信息,如果使用 await,需要使用 try catch 来捕获异常。

        我们再定义一个 getRoleReq 函数来发起获取用户角色的请求,这个请求依赖登录请求返回的用户 id,形式和 loginReq 函数差不多。使用 async/await 要注意,await 只能出现在使用 async 修饰的函数或方法体内。

        最后我们调用这两个函数,还是使用.then 的方式,这样要比使用 async/await 的形式简单些。这里我们在几个地方打印几个标记,让大家看下执行顺序,可以看到,打印出来的数字,是按 1->2->3 的顺序打印出来的,这是因为代码执行到 console.log(1)后会等 await 修饰的异步代码执行完,才会往后执行,所以 3 在 2 后面执行。

        TypeScript 对于 async/await 的支持是在 1.6 版本开始的,但是这要求你代码的构建目标是"ES6";1.7 版本对原生支持 ES6 Generator 的引擎中支持了异步函数;2.1 版本可以将异步函数编译为 ES3 和 ES5。

本节小结

        本小节我们学习了如何使用Promise来保证异步代码的执行顺序,通常我们在调用多个接口,后面接口依赖前面接口返回的数据的时候会用到;还有就是比如confirm弹窗这种,需要在用户点击了“确定”或者“取消”之后才能执行一些逻辑,这种也适用。我们还学习了Promise的语法糖async/await,使用这种语法更为形象清晰,但是不好的地方在于需要使用try … catch来获取原本Promise使用catch获取的错误信息。

        本章到这里就结束了,下一章我们将学习《项目配置及书写声明文件》,更加偏实战了,所以前面这些基础知识一定要学扎实了,要经常复习运用下,不过我们后面的课程也会对前面部分知识进行巩固的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值