1

大致问题是:
通过map或者foreach循环的回调函数操作数组,回调函数内部有ajax异步函数,通过await同步的写法来调用的。console.log打印出数组跟实际渲染到view层的页面始终不一致。
后来改写成for循环就好了。

所以总结出来,基础知识,在理解map和foreach这个api不够深刻,也对await 关键词的使用场景理解不透彻(虽然是for循环,但作用域函数可以找到顶层的函数作用域,所以不会报错)

贴出代码:

错误代码

adBannerList.map(async (item) => {
  // 异步函数 转同步的写法
  let res = await this.getAliyunAds();
  // 异步函数执行成功后,改变此数组的值
  item.imageUrl = res.imgUrl;
  item.linkUrl = res.actUrl;
})
// map 循环结束后,再执行双向绑定
this.setData({
  bannerList: adBannerList,
});
console.log('bannerList', this.data.bannerList)

期望结果是map循环结束后,才会执行双向绑定setData函数。
实际结果是:还未等map循环完(还未等内部的await 后面的异步函数执行完),setData 就执行了。

先搞清下async-await:
后来查询资料:await是异步转同步的写法,但并不会阻塞主线程的同步进行的代码,只会阻塞异步代码
但看如下代码:

async function timeout(ms) {
  await new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}
 
async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}
 
asyncPrint('hello world', 3000);
// hello world (3s后被打印)

所以:

在async语句里,同步和await异步代码不会以Eventloop事件方式进行区分(也就不存在先执行同步 后执行异步的操作)带有await的异步和同步代码统一被视为同步代码,更准确的说是以同步式阻塞方式 从上至下依次执行

回到正题:
forEach、map这样的高级循环遍历函数,在循环的同时,是不能更改内部item对象的(map更改后,返回的是新数组,forEach是原数组被更改)。
推荐看forEach 和for循环的区别,这篇文章
for和forEach的区别
此时console.log 数组所看见的结果,跟view渲染呈现的结果不一致。
这里需要去深入理解下,引用类型和简单类型的console.log,以及console.log的机制。
推荐看这篇文章:console.log遇见的坑

正确代码:

for(let i = 0;i < adBannerList.length; i++) {
  let item = adBannerList[i]
  // 这里await关键词可以用是因为for循环内部没函数作用域,
  //所以会向上找函数作用域,只要有async就可以。
  let res = await this.getAliyunAds();
  item.imageUrl = res.imgUrl;
  item.linkUrl = res.actUrl;
}

this.setData({
  bannerList: adBannerList,
});

知识点总结:
1.map和foreach(应该还有更多的类似函数)的回调函数是一个同步函数,非异步函数。

需要升入的知识点:
1、console.log的机制。尤其是针对引用类型
2、类型map和foreach 这样的循环函数机制。

至于为何for循环可以解决我的业务问题,我其实还没完全搞懂。
其实查询资料发现,阮一峰在介绍async-await的时候,就举例
说过类似的问题。只是在运用在实际工作中,翻车后才能真正掌握其知识点,并尝试去理解为什么?
如果有读者知道怎么解释for和forEach 为何前者可以满足我的业务,欢迎留言。

摘抄阮一峰一章节的例子:
下面的代码也可以解决我的问题,其实无非就是接口都请求成功后,按顺序更改我的目标数组罢了。

async function dbFuc(db) {
  let docs = [{}, {}];
  let promises = docs.map((doc) => db.post(doc));

  let results = await Promise.all(promises);
  console.log(results);
}
// 或者使用下面的写法 
async function dbFuc(db) {
  let docs = [{}, {}];
  let promises = docs.map((doc) => db.post(doc));

  let results = [];
  for (let promise of promises) {
    results.push(await promise);
  }
  console.log(results);
}

参考文献:
阮一峰async-await方法
解释async-await


健儿
79 声望4 粉丝

掌握好原生js。