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

为何在函数中设置数组对象属性后,该属性值仍为undefined?

问题分析与解决方案

核心问题

你遇到的问题本质是异步操作的执行时机不匹配:getYoutubeTitle是异步函数,它的回调要等API请求完成后才会触发,但你的循环是同步执行的——循环跑完后立刻查看videos数组时,所有获取标题的异步请求都还没返回,所以title字段自然没被更新。

另外要说明:你用let声明循环变量i是正确的,每个循环迭代的i都是独立的块级作用域变量,不会出现经典的闭包变量共享问题,这部分代码没问题。

解决方案

方案1:用async/await改写(代码最清晰直观)

先把回调风格的getYoutubeTitle转换成Promise版本,再用async函数包裹循环,确保每一步异步请求完成后再继续:

// 封装Promise版本的标题获取函数
function getYoutubeTitlePromise(videoId, apiKey) {
  return new Promise((resolve, reject) => {
    getYoutubeTitle(videoId, apiKey, (err, title) => {
      if (err) reject(err);
      else resolve(title);
    });
  });
}

// 异步更新视频数组
async function updateVideos() {
  for (let i = 0; i < videos.length; i++) {
    const videoId = YouTubeGetID(videos[i].link);
    // 等待标题请求完成
    try {
      const title = await getYoutubeTitlePromise(videoId, ytApiKey);
      videos[i].title = title;
    } catch (err) {
      console.error(`第${i+1}个视频获取标题失败:`, err);
    }
    // 同步更新缩略图
    videos[i].thumbnail = `http://img.youtube.com/vi/${videoId}/default.jpg`;
  }
  // 所有操作完成后再输出数组
  console.log(videos);
}

// 执行更新
updateVideos();

方案2:用Promise.all并行处理(效率更高)

如果不需要按顺序请求,可以同时发起所有API请求,用Promise.all等待全部完成,速度更快:

function getYoutubeTitlePromise(videoId, apiKey) {
  return new Promise((resolve, reject) => {
    getYoutubeTitle(videoId, apiKey, (err, title) => {
      if (err) reject(err);
      else resolve(title);
    });
  });
}

async function updateVideos() {
  // 为每个视频生成异步处理的Promise
  const updatePromises = videos.map(async (video) => {
    const videoId = YouTubeGetID(video.link);
    video.thumbnail = `http://img.youtube.com/vi/${videoId}/default.jpg`;
    try {
      const title = await getYoutubeTitlePromise(videoId, ytApiKey);
      video.title = title;
    } catch (err) {
      console.error(`视频${video.link}获取标题失败:`, err);
    }
  });

  // 等待所有异步操作完成
  await Promise.all(updatePromises);
  // 此时所有标题都已更新
  console.log(videos);
}

updateVideos();

方案3:保留回调风格(无需改Promise)

如果不想改动原有的回调写法,可以通过计数跟踪所有请求的完成状态,等全部完成后再访问数组:

let completedRequests = 0;
const totalVideos = videos.length;

for (let i = 0; i < totalVideos; i++) {
  const videoId = YouTubeGetID(videos[i].link);
  getYoutubeTitle(videoId, ytApiKey, (err, title) => {
    if (err) {
      console.error(`第${i+1}个视频获取标题失败:`, err);
    } else {
      videos[i].title = title;
    }
    // 每完成一个请求计数+1
    completedRequests++;
    // 所有请求完成后再输出数组
    if (completedRequests === totalVideos) {
      console.log(videos);
    }
  });
  videos[i].thumbnail = `http://img.youtube.com/vi/${videoId}/default.jpg`;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 13:57:16