Js 异步请求按顺序调用解决方案(真实工作场景,axios、Promise、async await)
需求背景:现在需要调用多个异步请求,要求某个请求调用成功之后,接着调另外的请求,有时候需要可能需要用上一个请求得到的结果,作为下一个请求的参数或者条件,继续调用另一个请求。
演示准备:vue项目、需要异步请求axios
安装axios
npm install axios --save
全局使用
//main.js
import axios from 'axios'
Vue.prototype.$axios = axios
实现一:Promise
Promise 是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复杂的异步任务。由于 Promise 是 ES6 新增加的,所以一些旧的浏览器并不支持,苹果的 Safari 10 和 Windows 的 Edge 14 版本以上浏览器才开始支持 ES6 特性。
构造Promise
new Promise(function (resolve, reject) {
// 要做的事情...
});
实际使用
为了方便演示,在vue项目的某个组件的mounted阶段直接使用
假设现在有两个接口,实现按顺序调用,最容易想到的做法为先调用第一个,再调用第二个:
let url = `https://api.apiopen.top/singlePoetry`;
this.$axios.get(url).then((res) => {
console.log(res, "res------");
});
let url2 = `https://api.apiopen.top/getJoke?page=1&count=2&type=video`
this.$axios.get(url2).then((res2) => {
console.log(res2, "res2------");
});
查看控制台打印结果
这样看起来貌似没什么问题,也按顺序输出了。当然,这是理想情况,是因为两个接口都响应的很快,几乎一调用就能返回,所以给我们一种感觉就是按顺序输出了。但是试想一下,如果第一个接口调用的过程中,受到了阻碍,亦或是查询的数据比较耗时,那么结果可能就不一样了。
由于实例中的接口均响应比较快,所以现在在接口一返回的地方增加一个setTimeout,模拟耗时查询(可能不太正确,但是这不是重点)。
let url = `https://api.apiopen.top/singlePoetry`;
this.$axios.get(url).then((res) => {
setTimeout(()=>{
//模拟耗时操作,需要1s后才返回数据
console.log(res, "res------");
},1000)
});
let url2 = `https://api.apiopen.top/getJoke?page=1&count=2&type=video`; this.$axios.get(url2).then((res2) => {
console.log(res2, "res2------");
});
这个时候控制台打印顺序发生了变化,先输出2,后输出1
虽然接口一写在前面,但是由于其比较耗时,导致先输出接口二的结果,显然这不满足我们最开始的需求。那么如何在不确定接口返回时间的情况下,控制接口输出的顺序呢?这时候就该使用第一种实现方案了--Promise。
在Promise中,使用then()控制执行顺序,当Promise中执行了resolve(),才会继续执行then()里面的,因此这个时候可以把接口一放到Promise里面,接口二放入then()里面,当接口一请求成功之后执行resolve(),才会继续往下调用then()里面的接口二。
new Promise((resolve, reject) => {
let url = `https://api.apiopen.top/singlePoetry`;
this.$axios.get(url).then((res) => {
setTimeout(() => {
//模拟耗时操作
console.log(res, "耗时返回的res");
resolve("success");
}, 1000);
})
}).then(()=>{ //上一个接口成功resolve之后执行then()
let url2 = `https://api.apiopen.top/getJoke?page=1&count=2&type=video`;
this.$axios.get(url2).then((res2) => {
console.log(res2, "res2------");
})
})
控制台按顺序输出,可以实现需求。
那么如果存在两个接口以上,同样需要按顺序,仍然可以使用Promise,但是注意接口二需要返回一个Promise对象。
new Promise((resolve, reject) => {
let url = `https://api.apiopen.top/singlePoetry`;
this.$axios.get(url).then((res) => {
setTimeout(() => {
//模拟耗时操作
console.log(res, "耗时返回的res");
resolve("success");
}, 1000);
})
}).then(()=>{
return new Promise((resolve,reject)=>{ //注意该处需要返回一个Promise对象
let url2 = `https://api.apiopen.top/getJoke?page=1&count=2&type=video`;
this.$axios.get(url2).then((res2) => {
console.log(res2, "res2------");
resolve("success");
})
})
}).then(()=>{
//其他接口,此处用setTimeout模拟异步请求
setTimeout(()=>{
console.log('res3------')
},100)
})
控制台打印输出结果,实现按顺序。
如果有3、4、5个甚至更多的呢,同样的写法,除了最后一个(当然最后一个也可以返回),中间的都需要返回一个Promise对象,因为这样才能使用then()方法实现按顺序执行,并且每个接口返回成功之后执行resolve()。
更多细节请参考JavaScript Promise | 菜鸟教程
以上的Promise在调用的接口数量少还可以使用,但是当接口很多,使用其就显得有点繁琐,层层回调,导致回调炼狱。在真实工作场景中一般使用相对简单的方式(也是最容易实现的方式)。
使用axios时每一个异步请求都有一个then()方法,代表请求成功,那么我们可以在调用第一个接口成功时,也就是在每个axios的then()里面再去调用下一个接口。
let url = `https://api.apiopen.top/singlePoetry`;
this.$axios.get(url).then((res) => {
console.log(res, "真实工作场景!");
let url2 = `https://api.apiopen.top/getJoke?page=1&count=2&type=video`;
this.$axios.get(url2).then((res2) => { //在第一个接口请求完成之后调用下一个,保证顺序
console.log(res2, "res2------");
//继续调下一个接口......
});
});
控制台打印输出结果
扩展:Generator(了解,一个演变过程)
Generator 顾名思义是生成器,那什么是生成器?官网给出的解释: 使用 function* 语法和一个或多个 yield 表达式以创建一个函数即为生成器,当然它的返回值就是一个迭代器即生成器。生成器可以用来控制执行顺序。Generator的关键是yield跟next()。
详细内容参考Generator - JavaScript | MDN
举个简单的例子
function *gen(){
let y = yield 1 + 1;
let z = yield y + 1;
return z;
}
let step = gen()
console.log('generator',step.next())
控制台打印输出情况
返回的是一个指针对象,done为false表示还没迭代完成。
此时done仍未false,继续完善代码。
function *gen(){
let y = yield 1 + 1;
let z = yield 2 + 1;
return z;
}
let step = gen()
console.log('generator',step.next())
console.log('generator',step.next())
console.log('generator',step.next())
done为ture表示迭代完成,此时value为undefined,继续执行next()结果不变。
实现二:async await
async awiat可以说是promise的改进方案,可以让异步执行的代码看起来同步化,解决了promise的回调炼狱问题,如下对promise的实例进行改造。
promise写法
new Promise((resolve, reject) => {
let url = `https://api.apiopen.top/singlePoetry`;
this.$axios.get(url).then((res) => {
setTimeout(() => {
//模拟耗时操作
console.log(res, "耗时返回的res");
resolve("success");
}, 1000);
})
}).then(()=>{
let url2 = `https://api.apiopen.top/getJoke?page=1&count=2&type=video`;
this.$axios.get(url2).then((res2) => {
console.log(res2, "res2------");
})
})
如果是vue项目,在methods中分别封装异步请求接口为单独方法,里面返回Promise对象,注意异步请求成功后记得resolve()。
step1() {
return new Promise((resolve, reject) => {
let url = `https://api.apiopen.top/singlePoetry`;
this.$axios.get(url).then((res) => {
setTimeout(() => {
//模拟耗时操作
console.log(res, "耗时返回的res,使用async await");
resolve("success");
}, 1000);
});
});
}
step2() {
return new Promise((resolve, reject) => {
let url2 = `https://api.apiopen.top/getJoke?page=1&count=2&type=video`;
this.$axios.get(url2).then((res2) => {
console.log(res2, "res2------");
resolve("success")
});
});
}
使用async await,在methods中添加asyncFun方法,await接收到resolve()之后,继续往下执行,否则会一直阻塞下面代码的执行(如reject或者请求error的情况)。
async asyncFun(){
await this.step1();
await this.step2();
//如有多个,继续执行await fun()
//......
}
在mounted阶段执行该async方法
this.asyncFun()
控制台打印输出结果
满足异步请求按顺序执行的需求,若有更多个,继续将下个请求封装成一个单独方法,里面返回Promise对象,请求成功需执行resolve() ,然后在async方法里面再加一个await,调用方法。在实际的工作中,视情况使用promise、async await,当然,也可以直接在异步请求成功then()里面继续发送下一个请求,这也不失为一个好方法。
相关文章
- js 生成4位随机数及Js知识汇总
- JS 逆向 --- 过无限debugge、hook、js混淆还原、控制流混淆
- [转] js async await 终极异步解决方案
- 拥抱Node.js 8.0,N-API入门极简例子
- h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编程 vue2入坑随记(二) -- 自定义动态组件 微信上传图片
- 【Vue/js】Js中执行变量中的命令语句,也就是所谓的宏替换(很实用的例子)
- js异步下载文件请求
- 43dwr - util.js 功能(useLoadingMessage)
- JS 的同步与异步
- [转]如何判断js中的数据类型
- js遍历键值对
- JS的加载方式---同步和异步
- ASP.NET MVC异步验证是如何工作的03,jquery.validate.unobtrusive.js是如何工作的
- => js 中箭头函数使用总结
- JavaScript setTimeout用法,js setTimeout带参数
- 演示js异步,同步请求,响应解码
- JS教程之 JavaScript 框架之战已经结束,而且只有一个赢家
- 聊聊JS动画库:Velocity.js
- JS: javascript 点击事件执行两次js问题 ,解决jquery绑定click事件出现点击一次执行两次问题
- 详解回调函数——以JS为例解读异步、回调和EventLoop
- js获取日期:昨天今天和明天、后天
- 微信小程序表单校验WxValidate.js使用
- 在Nuxt.js如何使用Vuex最新教程
- 【HarmonyOS】【JS】【布局】鸿蒙js开发input 输入框弹出输入法时上方布局被挤扁?
- 【HarmonyOS】【JS】 鸿蒙js开发使用div自带的scroll,滑动条拉不到最下面?
- js模版引擎handlebars.js实用教程——由于if功力不足引出的Helper
- JS中的深拷贝和浅拷贝
- 封装异步API-js先执行完一个函数后再执行下面的逻辑或者方法