在日常JS编程中,会经常遇到异步逻辑,就是需要等待某条件成立之后再执行的代码,这种情况经常出现在ajax请求中,所以着重用ajax请求作为例子讲解。 ### 回调函数 这是我们用的最多的方法,通过指定一个函数或匿名函数,在异步完成后调用这个回调函数(callback function)。 这种方法在web应用初期逻辑简单情况下其实也够用,例如做一些简单的表单操作和提交。随着web应用的复杂化,弊端也暴露出来,常见的问题就是回调地狱的问题,造成代码杂乱、闭合符混乱,代码写起来极不优雅。 下面用JQ框架中常用的ajax请求为例: ``` $.get("/test1", function (res1) { $.get("/test2", function (res2) { $.get("/test3", function (res3) { $.get("/test4", function (res4) { console.log(res4); }); }); }); }); ``` ### ES6给出的方案 ES6标准的提出解决了许多以往的不足,针对回调地狱的问题提出的方案是`Promise`。通过创建一个`Promise`对象,在异步完成之后进行链式回调,与以往相比解决了回调地狱问题,语法也十分优雅。 Axios就是一个基于Promise的请求库,下面用它来举例 ``` axios.get('/test1') .then(res => { return axios.get('/test2') }) .then(res => { return axios.get('/test3') }) .then(res => { return axios.get('/test4') }) .then(res => { ... }) ``` 除了解决回调问题,它还提供了`Promise.all`和`Promise.race`的方法,对于并发请求的处理提供了高效的方案。 ### ES7给出的终极方案 上面ES6给出的`Promise`方案说到底还是回调的形式,只不过是链式回调,在异步编程中并不是最完美的。终极的方案应该是使同步异步代码统一,即是让异步的代码变成和同步代码一样书写。ES7提出的`async`与`await`则能够彻底解决回调地狱问题。 通过`async`定义一个函数,我们把这个叫做异步函数,这个函数内可以使用`await`去等待一个`Promise`异步成功,只有当异步成功后才能继续执行下面的代码。 同样是ajax请求的情况,ES7中可以这么写: ``` (async function () { const res1 = await axios.get('/test1'); const res2 = await axios.get('/test2'); const res3 = await axios.get('/test3'); const res4 = await axios.get('/test4'); console.log(res1, res2, res3, res4); })() ``` 说到底这个就是`Promise`实现的语法糖,只有当`await`后面的`Promise`状态被改变才会通过=号赋值给变量,然后继续往下面执行,在语法上就和同步代码是一样的了。 这个方案是最被推荐的,目前兼容性还不佳,但是在前端领域的未来几年内一定会成为主流方案。

异步编程解救指南

413 4 前端 2021-02-09