前言
promise和async await在前端开发中非常重要,下面我就用一个案例演示一下他们的用法和联系,在此之前你需要了解Es6中的promise。
案例
我们先来看一个案例:假设有服务端接口:http://loclahost:3000?type=a,http://loclahost:3000?type=b,http://loclahost:3000?type=c
,分别返回a,b,c类商品数量,如何获取a、b、c商品的总和?
//服务端代码(后端提供,不需要会写),我只是模拟一下接口
const http = require('http');
const url = require('url');
http.createServer((req,res)=>{
var type =url.parse(req.url,true).query.type;
res.writeHead(200,{"Access-Control-Allow-Origin":"*"})
if(type=='a'){
res.write(JSON.stringify({type:'a',count:30}));
}else if(type=='b'){
res.write(JSON.stringify({type:'b',count:20}));
}else if(type=='c'){
res.write(JSON.stringify({type:'c',count:40}))
}
res.end()
}).listen(3000);
有人说那还不简单,掏出键盘噼里啪啦
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="axios.min.js"></script>
</head>
<body>
<script>
var total = 0;
axios.get(
"http://localhost:3000",
{params:{type: 'a'}}
).then(res=>{total+=res.data.count});
axios.get(
"http://localhost:3000",
{params:{type: 'b'}}
).then(res=>{total+=res.data.count});
axios.get(
"http://localhost:3000",
{params:{type: 'c'}}
).then(res=>{total+=res.data.count});
console.log(total)
</script>
</body>
</html>
有人这样想吗?想的挺美的,很显然输出为0,异步请求数据,你打印total 时数据还没回来呢。那怎么办呢?往下看你还有救。
解决方法(一)回调地狱
首先介绍一个比较麻烦的方法,回调地狱,不推荐,但也能解决问题
- 缺点:可维护性差,执行效率低,代码难看
- 优点:没有(促进脱发哈哈)
运行之后出来了结果90,看了半天不知道哪个括号对哪个,不说它了<script> var total = 0; axios.get( "http://localhost:3000", {params:{type: 'a'}} ).then(res=>{ total+=res.data.count; axios.get( "http://localhost:3000", {params:{type: 'b'}} ).then(res=>{ total+=res.data.count; axios.get( "http://localhost:3000", {params:{type: 'c'}} ).then(res=>{ total+=res.data.count; console.log(total) }); }); }); </script>
解决方法(二)Promise
三个请求串行,发一个请求加一次count。封装返回Promise对象的函数,通过then接力的方式累加。代码如下,备注尽力详细了,好好理解一下。
<script> var total = 0; //封装返回Promise对象的函数 function getCount(type){ return new Promise( function(resolve,reject){ axios.get( "http://localhost:3000", {params:{type}} ).then(res=>{ //向外抛出,相当于return但Promise里没有return resolve(res.data.count); }); } ) }; // getCount(a)return出来了自己的promise,可以接then getCount('a').then( // promise里的resolve参数会交给count function(count){ total+=count; // 把getCount('b')的new Promise()返回 return getCount('b') } ).then(// getCount(a)return出来了getCount(b)的promise,可以继续接then function(count){ total+=count // 把getCount('b')的new Promise()返回 return getCount('c') } ).then(// getCount(b)return出来了getCount(c)的promise,可以继续接then function(count){ total+=count; console.log(total) } ) </script>
三个请求并行,等所有请求都执行完再把所有的count汇总
<script> var total = 0; //封装返回Promise对象的函数 function getCount(type){ return new Promise( function(resolve,reject){ axios.get( "http://localhost:3000", {params:{type}} ).then(res=>{ //向外抛出,相当于return但Promise里没有return resolve(res.data.count); }); } ) }; Promise.all([ getCount('a'),//return getCount(a)的new Promise()对象 getCount('b'),//return getCount(b)的new Promise()对象 getCount('c')//return getCount(c)的new Promise()对象 ]).then(result=>{//all后面的then会在所有new Promise()执行完之后执行 // result是三个Promise对象resolve抛出的值所组成的数组,并且值的顺序和调用顺序一样 // 不管谁先谁后执行完,结果都是[a,b,c] total = result.reduce((prev,elem)=>prev+elem)//reduce汇总 console.log(total) }) </script>
总结:上面案例中a、b、c的执行顺序不影响总数total,所以用并行比较好,并行中的请求没有先后顺序,三个请求之间没有联系,执行时间以最慢的那一个请求为准。而串行必须一个一个执行,执行时间是三个请求时间的总和,所以并行效率比较高。但公司中也会出现必须串行的情况,比如打电话,必须一个电话挂了才能接下一个。
解决方法(三)async await
牢记:
- await必须在前面有async的函数内使用(所以我用匿名函数将异步操作包裹)
- await代替的是then,所以有then的地方前面全部可以换成await(所以Promise.all也可以用async await实现并行)
- Es7中的async await必须基于Es6的Promise,加了await的后序代码会被挂起,等函数执行完才会执行。
- await等的不是后面所跟的函数,而是函数返回的Promise
1. 串行:
<script> var total = 0; //封装返回Promise对象的函数 function getCount(type){ return new Promise( function(resolve,reject){ axios.get( "http://localhost:3000", {params:{type}} ).then(res=>{ //向外抛出,相当于return但Promise里没有return resolve(res.data.count); }); } ) }; (async function(){ var count = await getCount('a') total+=count;//等到await后面的异步函数执行完再累加 count = await getCount('b') total+=count; count = await getCount('c') total+=count; console.log(total) })() </script>
2. 并行:
先写到这里了,希望能对大家有所帮助,有问题欢迎交流个人博客<script> var total = 0; //封装返回Promise对象的函数 function getCount(type){ return new Promise( function(resolve,reject){ axios.get( "http://localhost:3000", {params:{type}} ).then(res=>{ //向外抛出,相当于return但Promise里没有return resolve(res.data.count); }); } ) }; (async function(){ //await等的是三个请求,等三个请求全部执行结束才能执行后序代码 var result = await Promise.all([getCount('a'),getCount('b'),getCount('c')]) total = result.reduce((prev,elem)=>prev+elem)//reduce汇总 console.log(total) })() </script>