You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

非async函数为何异步执行?Spotify API校验时artists数组为空

问题原因

你遇到的artists数组始终为空的核心原因是没有处理异步操作的时序问题,具体遗漏的逻辑点:

  • fetch是异步非阻塞API,发起请求后会立刻继续执行后续代码,不会等接口返回再往下走。你写的validateArtist没有返回内部的Promise链,外层完全没法感知请求什么时候完成
  • setArtists里用forEach循环调用校验函数时,既没有收集异步任务,forEach本身也不会等待内部异步操作执行完,调用完setArtists的瞬间所有接口请求还在排队,根本没来得及往数组里push数据
  • 接口返回401时的refreshToken调用也没有等待执行完成,就算加了等待逻辑,这个分支也会因为没有重发请求导致对应歌手校验丢失
  • 你直接把全局artists数组赋值给user.artistiPreferiti的时机太早,这时候所有接口都还没返回结果,数组自然是空的。就算后续数组push了内容,如果你后续逻辑是在赋值后立刻用user,也拿不到填充后的值。
修复方法

首先调整validateArtist,让它返回校验结果的Promise,补全401后重发请求的逻辑,注意对搜索关键词做编码:

async function validateArtist(s) {
  let token = localStorage.getItem("Token")
  // 对关键词做URL编码,避免特殊字符、空格导致请求异常
  const url = `https://api.spotify.com/v1/search?type=artist&q=${encodeURIComponent(s.trim())}`
  try {
    let res = await fetch(url, {
      method: "GET",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
        Authorization: `Bearer ${token}`,
      },
    })
    // 处理token过期场景,刷新后重发请求
    if (res.status === 401) {
      // 注意:你自己实现的refreshToken需要返回Promise,确保token刷新完成并写入localStorage后再resolve
      await refreshToken(s)
      token = localStorage.getItem("Token")
      res = await fetch(url, {
        method: "GET",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
      })
    }
    const result = await res.json()
    return result.artists.items.length ? s.trim() : null
  } catch (err) {
    console.error("歌手校验失败:", s, err)
    return null
  }
}

然后重写setArtists,去掉全局变量依赖,等待所有校验请求完成后返回有效结果:

async function setArtists(inputEl) {
  const artistList = inputEl.value
    .split(",")
    .map(item => item.trim())
    .filter(Boolean) // 过滤掉空输入
  // 并行发起所有校验请求,等待全部执行完成
  const validateResults = await Promise.all(
    artistList.map(artist => validateArtist(artist))
  )
  // 过滤掉校验不通过的null值,返回有效歌手列表
  return validateResults.filter(Boolean)
}

最后调整调用逻辑,必须等异步校验全部完成拿到结果后,再生成user对象,所有依赖user的后续逻辑都要放在这个异步流程之后:

// 这段代码需要放在async函数中,如果你不想改外层函数,也可以用.then接后续逻辑
const validArtists = 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,
  artistiPreferiti: validArtists
}
// 后续提交、存储user的逻辑全部写到这里

注意:异步场景下尽量不要用全局可变变量做状态传递,很容易出现时序不可控的问题,直接通过async/await拿函数返回值的方式逻辑会清晰很多,也不会出现竞态问题。

内容的提问来源于stack exchange,提问作者Giackkk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 15:00:50