我们知道,在promise对象的then方法可以处理onfulfilled和onrejected两个事件监听回调,但是我们一般采用catch来处理onrejected的监听回调,因为catch可以捕获部分程序异常;有利于程序的健壮性。例如:
function getBanner() { let p = new Promise((resolve, reject) => { $.ajax({ type: "GET", url: "http://localhost:3000/api/index/banner", success: function (response) { resolve(response); }, error: function (err) { reject(err); } }); }); return p; } let p = getBanner() .then(rst => { return rst; }) .catch(err => { console.log(err); });
我们通过jQuery的ajax来向服务器发起轮播图数据的请求,上面代码若是正确的执行则会进入then方法里处理,若是异常的,也就是说必然进入catch环节,这代码看起来并没有什么,好像也并不复杂。
但是,如果在异步请求过程中出现了几个请求直接有依赖关系,则使用这种解决方案就复杂得多了。例如:
$.ajax({ url: "http://www.ujiuye.tech/:3000/api/firstCategory", // 所有一级分类 dataType: "json", success(res) { $.ajax({ url: `http://www.ujiuye.tech/:3000/api/secondCategory`, // 传递一级ID换取下属的二级分类列表 data: { firstId: res['list'][0][0]['firstId'] }, dataType: "json", success(res2) { $.ajax({ url: `http://www.ujiuye.tech/:3000/api/thiredCategory`, // 传递二级分类ID, 获取下属的三级分类列表 data: { secondId: res2['list'][0]['secondId'] }, dataType: "json", success(res3) { $.ajax({ url: `http://www.ujiuye.tech/:3000/api/categoryList`,// 传递三级分类ID, 获取下属的商品数据列表 data: { thiredId: res3['list'][0]['thiredId'] }, dataType: "json", success(result) { console.log(result); } }) } }) } }) } })
在小U商城项目中的商品列表页面,我们同时要发起四个请求来分别获取:一级分类、二级分类、三级分类和商品,那么这是时候利用回调函数会出现”回调地狱”的现象,即使是使用promise来优化,也会出现大量的代码嵌套,这样的代码会容易让人疑惑,而且也不利于后续的开发维护。所以我们可以使用async...await来优化这些。
例如上面请求轮播图的例子,使用async和await来优化之后:
function
getBanner() { let p = new Promise((resolve, reject) => { $.ajax({ type: "GET", url: "http://localhost:3000/api/index/banner", success: function (response) { resolve(response); }, error: function (err) { reject(err); } }); }); return p; } async function getData(){ let data=await getBanner(); console.log(data); }
这样的代码相比于上面的代码要简化很多,但是也有弊端,由于await只能接收promise的成功结果,也就是说,若上面代码出现了异常,则代码会中断执行。作为一个优秀的开发人员肯定不希望代码崩掉,那么该如何解决异常呢,有两种方案:一是采用try..catch来捕获异常,另外是使用catch
async function getData() { try { let data = await getBanner(); console.log(data); } catch (e) { console.log(e); } } //或者 async function getData() { let data = await getBanner().catch(e => { console.log(e); }) console.log(data); }
但这两种方案都又会出现嵌套,特别是若发起一些负责的请求(例如上面回调地狱的情况),则代码依然非常复杂,那么有没有更好的解决方案呢。答案是,必须有啊。在项目开发过程中,我们经常使用await-to-js的技术来解决这个问题。上干货:
function to(p) { return p .then(data => [null, data]) .catch(err => [err, null]); }
其实这个方案依然是利用promise的链式调用原理来解决的。那么使用,最后代码为:
function to(p) { return p .then(data => [null, data]) .catch(err => [err, null]); } async function getData() { let [err, data] = await to(getBanner()) }
利用这个方案,大家发现,代码量不仅大大的减少,而且兼容性更加友好。特别是用来处理回调地狱的情况,简直不要太舒服。