大致問題是:
通過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