各位了解过Promise的朋友都知道,这是一个等待异步请求返回的工具函数。
由于js是单线程的,所以想要获取异步函数返回的信息需要使用Promise,async await,generator这样的异步函数
让你的代码能够等待异步返回,同步进行。
那么如果我用map函数或者for循环进行异步请求怎么办呢?
我们先来看一个例子。
<template>
<div class="homepage">
<h3>异步获取展示案例</h3>
<ul>
<li v-for="(item, index) in data" :key="index">
{{ item }}
</li>
</ul>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
const dataList = [
[100, 200],
[100, 250],
[300, 250],
[300, 200],
[300, 400],
[200, 350],
[200, 400],
];
onMounted(async () => {
data.value = await getData(dataList);
});
function promiseFn() {
return new Promise((res) => {
setTimeout(() => {
res("then over");
}, 200);
});
}
const data = ref([]);
async function getData(list) {
const resData =
await list.map(async item=> {
const res = await promiseFn()
item.push(res)
return item;
})
console.log("获取数据 -->",resData);
return resData
}
</script>
上面这个例子演示了一个异步获取数据的场景。我的目的是根据 dataList 这个数组里的数值获取到图片并且展示在页面上。
但是现在出现了一个很奇怪的错误
原本的内容都变成了Promise对象,我明明已经用了async await来异步获取了,为什么它返回的是一个这样的东西呢?
我们先理解一下map函数的原理吧。
它本身是用for循环封装的。
function map(List, fn) {
let element;
for (let i = 0; i < List.length; i++) {
element = List[i];
fn && fn(element, i);
}
return List;
}
onMounted(async () => {
// data.value = await getData(dataList);
data.value = await map(dataList, async (item) => {
const res = await promiseFn();
item.push(res);
console.log(item);
return item;
});
console.log(data.value);
});
上面我实现了一个map函数,接收一个数组和一个函数。
并且让他每一个item 加入 promiseFn的返回值
然后我们来看一下他运行后的结果。
它确实被改变了数值,当然为什么会被改变数值这里牵扯到一个深拷贝浅拷贝的问题,我这里就不深究了。
诶,这个array的length是2,也就是被push之前的数量,但是最终打印出来的,却有被push的值。
并且map内的打印在外部之后
这是为什么?
可以看一下之前写的map函数,它内部其实是没有Promise等待返回的,
也就是说它并不会等待第二个参数 fn函数 的操作。
那么我们应该如何正确的获取到 fn 函数操作过后的值呢?
答案就是 Promise.all !
onMounted(async () => {
data.value = await getData(dataList);
console.log(data.value);
});
async function getData(list) {
const resData = await Promise.all(
list.map(async (item) => {
const res = await promiseFn();
item.push(res);
console.log('map - item -->',item)
return item;
})
);
console.log("获取数据 -->", resData);
return resData;
}
将getData里 list.map的操作用Promise.all包裹起来
然后看下结果
可以看到我刷新页面后,map里的操作是被等待了的,并且数据也同步更新了。
这看起来是我们预期中想要得到的结果。
上面描述了如何在数组循环中使用Promise.all等待异步操作。
这个可以用在需要使用 数组 内部某项值 循环查询 接口并返回数据的场景。
比如element ui 的 table 里的 expand 插槽中的数据 , 在获取tableData的接口本身并没有返回,
需要根据 列表项 ID 或 其他数据 查询并返回的时候用到。
const BaseUrl = 'http://www.baidu.com/'
const Data = await axios.get(BaseUrl + 'userList')
table.data = await Promise.all(
Data.map(item=>{
const res = await axios.get(BaseUrl + 'userList/' + item.id)
return res
})
)
就类似于上面这个样子。
好了,这次分享就到这里了。
这里是海星吧,我们下次见。
(如果有看到我之前爆炸了的部分的小伙伴可以不用等我把二进制转换为图片了。)
最后于 2022-4-12
被海星吧编辑
,原因: 完成帖子。