一、前言
在开发中,可能难免会遇到需要将某个数组遍历获取指定字段的值,然后用此值作为参数来调用API的场景。那么,JS如何在循环语句中调用API并且保证同函数作用域中的其他代码同步执行,并且能使用循环语句中多次调用API请求到的全部数据?我们一起来看看:
二、问题描述、分析和解决
2.1、问题描述
这几天,在进行一个使用到百度地图多点标注的功能开发,标注点的数量,经纬度坐标值,以及其他数据,均需动态用API获取。此API有一个参数,需从一个对象数组([{},{},{},…])中获取,这个数组中的每个对象元素中有此API的参数,所以需要用到循环遍历并调用API。如何在一个函数作用域中,先执行完这个含有多次接口调用的循环,再执行此循环语句后面的代码成了一个不大不小的问题。
2.2、问题分析(求道)
对于这种情况,我想要的执行逻辑是:每循环一次,得到一个value,然后用这个value作为参数调用一次API得到一条目标数据。最后,将在循环语句中多次调用API得到的数据,存储到sessionstorage中供在地图中打点标注。因为只有让此函数作用域中的代码都同步执行,才能保证最终数据的完整和正常。
所以,此问题解决思路的方向,就是要让所有的异步变同步,此函数作用域内,所有的代码同步执行。
2.3、问题解决(求术)
有术无道止于术,有道无术术可求。
有上面求道部分的支持,接下来解决这个问题就变得明了和直接了:
- 我计划用async await来使异步“变”同步;
- 找一个可以使用async await的循环语句(这个不是废话,诸君接着看);
好了,执行步骤就是这两步,接下来直接上代码:
...// 因为我是基于React开发,所以函数定义形式只供参考。async关键字登场const recycleGetData = async (data) => {const projAddInfo = [];// 用async await保证有异步调接口的循环体变同步,对循环语句种类有一定限制,我这里使用的是for循环,用for of循环替代fro循环也可以达到预期效果,别的循环诸君也可以试试,map是不可以的,我试过了。for (let i = 0; i < data.length; i++) {// await在此处登场,这不仅可以保证for循环内代码同步执行,还可以使整个函数作用域内的“代码块”同步执行。const apiResult = await yourAPIName(data[i].value);if (apiResult == null) {continue;}const obj = {};if (apiResult.Key === 'XXX') {const locationArr = apiResult.Value.split(',');obj['lng'] = locationArr[0];obj['lat'] = locationArr[1];obj['proAddress'] = locationArr[2];obj['projName'] = data[i].title ? data[i].title : data[i].label;...}projAddInfo.push(obj); }// 用async await控制过的for循环,便可保证先执行完for循环,拿到循环内每次调用接口调用的返回值,再执行下面的代码console.log(projAddInfo)};
...const function1 = async () => {...await yourAPIName1()...const data = [{XXX},{XXX},{XXXX},...];await recycleGetData(data);...}
...
从上面代码和其中的注释可以就可以理解,问题解决的第二步思路“ 找一个可以使用async await的循环语句”为什么我说它不是一句废话,因为有的循环语句是达不到我们的预期目的的(除了我试过的map语句,有的博主也提到了foreach语句也无法达到预期,诸君有谁感兴趣,可以一试)。
下面再来看看与本片内容主体相关的一些前端技术知识点,以作为本篇博文的扩充内容。
三、JS作用域、promise、async await知识扩展
3.1、JS作用域
JS的作用可以说是可访问变量的集合,其可根据范围分为局部作用域和全局作用域,函数作用域是局部作用域。为了和本篇博文主要内容贴合,关于局部作用域,在本博文中,用函数作用域来管中窥豹。
- 全局作用域
- 直接写在script标签的JS代码,都在全局作用域;
- 全局作用域在页面打卡的时候创建,在页面关闭时销毁;
- 全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问到。
- 函数作用域(是局部作用域)
- 每调用一次函数就会创建一个新的函数作用域,它们之间是相互独立的;
- 调用函数时创建函数作用域,函数执行完毕以后,作用域销毁;
- 在函数作用域中可以访问全局变量,但在全局作用域中无法访问到局部变量。
3.2、promise
我们肯定都听过“回调地狱”这个术语,它其实就是JS 处理异步时使用回调函数,一个回调函数执行完成,进行下一个回调函数。这样会导致层层嵌套,代码不清晰。这是在promise出现之前,JS对异步的处理方式。promise的出现,直接让我们可以避开代码不清晰的“回调地狱”。一起来了解一下promise的一些基本知识吧:
- promise的状态
promise 有三种状态 :pending(进行中),resolved(成功),rejected(失败),并且它的状态是不可逆的。不可逆就是pending—>resolved或pending—>rejected。 - promise 有两个回调函数
promise 的参数是一个函数,函数里还有两个参数:resolved,rejected。- resolved(res) 处理成功的 函数 它传递的参数 会在then方法里输出;
- rejected(err) 处理失败的函数 它传递的参数 会在 catch方法里输出。
- 多个promise的执行
- promise.all(多个promise同时执行):它的参数是多个promise函数,直到最慢的一个promise执行完毕才返回所有的promise的结果,有一个promise函数崩溃,整个promise就崩溃,故要慎用;
- promise.race:多个promise 执行速度pk看谁的速度最快,返回最快的promise的结果。
3.3、async await
- async
带async关键字的函数,是声明异步函数,返回值是promise对象,如果async关键字函数返回的不是promise,会自动用Promise.resolve()包装。 - await
带await关键字的语句,等待右侧表达式的结果,这个结果是promise对象或者其他值。
如果它等到的不是一个 promise 对象,那 await 表达式的运算结果就是它等到的东西。
如果它等到的是一个 promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
四、说明
参考链接:
JavaScript函数作用域
js中数组for循环调用后端接口,返回数据填充入数组
JS中async与await详解
js promise 详解
欢迎大家一起讨论、学习