JavaScript async函数await fetch未等待致artists数组为空修复
问题说明
对接Spotify API实现艺术家信息校验功能时,运行后artists[]始终为空,本质是异步执行时序错误:fetch请求未执行完成时,就提前完成了user变量的赋值,此时artists数组还未完成填充。
原有实现代码:
let artists = [] function setArtists(input) { artists.length = 0 let parsedInput = input.value.split(",") parsedInput.forEach(artist => { validateArtist(artist) }) } async function validateArtist(s) { let token = localStorage.getItem("Token") let url = "https://api.spotify.com/v1/search?type=artist&q=" + s console.log(url) await fetch(url, { "method": "GET", "headers": { 'Accept': 'application/json', 'Content-Type': 'application/json', "Authorization": "Bearer " + token, } }) .then(response => { if (response.status === 401) { refreshToken(s) } return response.json() }) .then(searchedResults => searchedResults.artists.items.length != 0) .then(isArtist => { if (isArtist) { artists.push(s) } }) }
原有调用逻辑:
setArtists(document.getElementById("artistiPreferiti")) var user = { username: document.getElementById("username").value, email: document.getElementById("email").value, password: document.getElementById("password").value, gustiMusicali: document.getElementById("gustiMusicali").value, artistiPreferiti: artists }
问题根因
setArtists是同步函数,内部通过forEach调用异步的validateArtist时,没有接收、等待异步任务的执行结果,触发所有校验请求后就直接退出了函数- 外层构造user对象是同步逻辑,调用
setArtists后不会等待所有接口返回,直接读取了当前还是空值的artists数组赋值 - 接口返回401时调用
refreshToken没有加await,即使触发token刷新,也不会等待刷新完成就继续执行后续逻辑,鉴权流程存在时序漏洞 - 搜索关键词未做URL编码,艺术家名称包含空格、特殊符号时请求会异常
修复方案
- 将
setArtists改为异步函数,用Promise.all配合map等待所有艺术家校验请求全部完成,替换无法处理异步等待的forEach写法 - 统一用
async/await写法替换原有的.then()链式调用,避免漏写等待导致时序错误 - 外层调用
setArtists时加await,等数组填充完成后再构造user对象 - 补全401场景下的等待、重试逻辑,对搜索关键词做URL编码,赋值时做数组浅拷贝避免引用类型副作用
修复后的完整代码:
let artists = [] async function setArtists(input) { artists.length = 0 // 拆分输入后顺手去除每个艺术家名前后的空格 let parsedInput = input.value.split(",").map(name => name.trim()) // 等待所有校验请求执行完成 await Promise.all( parsedInput.map(artist => validateArtist(artist)) ) } async function validateArtist(s) { let token = localStorage.getItem("Token") // 对搜索关键词做URL编码,避免特殊字符导致请求异常 let url = "https://api.spotify.com/v1/search?type=artist&q=" + encodeURIComponent(s) console.log(url) const response = await fetch(url, { method: "GET", headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', "Authorization": "Bearer " + token, } }) // 401时等待token刷新完成,再重试当前校验请求 if (response.status === 401) { await refreshToken(s) return validateArtist(s) } const searchedResults = await response.json() const isArtist = searchedResults.artists.items.length !== 0 if (isArtist) { artists.push(s) } }
修复后的调用逻辑(需要放在async函数内才能使用await):
async function handleSubmit() { // 等待所有校验完成、artists数组填充完毕 await setArtists(document.getElementById("artistiPreferiti")) const user = { username: document.getElementById("username").value, email: document.getElementById("email").value, password: document.getElementById("password").value, gustiMusicali: document.getElementById("gustiMusicali").value, // 浅拷贝数组,避免后续artists变动影响user对象的属性值 artistiPreferiti: [...artists] } // 后续执行提交等逻辑即可 }
注意事项
- 不要在循环中直接触发异步函数而不等待结果,
forEach本身没有返回值,无法配合await等待异步任务执行 - 混用
async/await和.then()链容易漏写等待逻辑,统一写法能大幅降低异步时序bug的概率 - JS中数组、对象属于引用类型,直接赋值时只是传递内存地址,后续原变量改动会同步影响赋值后的属性,必要时需要做浅/深拷贝
内容的提问来源于stack exchange,提问作者Giackkk
相关产品推荐
相关产品推荐

