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

使用Fetch API循环获取多URL HTML时键值对匹配错误如何修复

修复for循环中Fetch API异步导致的键值不匹配问题

你在for循环中使用Fetch API批量获取URL的HTML数据,初始pos_links_info结构如下:

link 0 : contains URL of first webpage
link 1 : contains URL of second webpage...//and so on

但实际输出出现键值对错位,比如title 0存储的是第二个网页的标题,body 0 x也是第二个网页的内容,和预期结构不符。

原代码

var sl_no = 0;
for(const [key,value] of Object.entries(pos_links_info)){

   //Here value is the URL of that website

   fetch('https://api.codetabs.com/v1/proxy?quest='+value)
      .then(
          function (response) {
             if (response.status !== 200) {
                 console.log('Unable to fetch data from url link: ' +response.status);
                 return;
             }
             response.text().then(function(data) {
                var date = new Date();
                var parser = new DOMParser();
                var htmldoc = parser.parseFromString(data, "text/html");
                var title;
                if(htmldoc.getElementsByTagName("h1")[0] != null)
                   title = htmldoc.getElementsByTagName("h1")[0].innerText;
                else
                   title = htmldoc.getElementsByTagName("title")[0].innerText;
                if(title != ""){
                   pos_links_info["title"+" "+sl_no]=title.trim();
                }
                if(htmldoc.getElementsByTagName("img") != null){
                    for(var l=0; l<htmldoc.getElementsByTagName("img").length; l++){ 
                       if(htmldoc.getElementsByTagName("img")[l].src.includes(value.split("https://")[1].split("/")[0]) && 
                           htmldoc.getElementsByTagName("img")[l].src.includes(date.getFullYear().toString())){
                              pos_links_info["image"+" "+sl_no] = htmldoc.getElementsByTagName("img")[l].src.trim();
                              break;
                       }
                    }
                }
                if(htmldoc.getElementsByTagName("p") != null){
                    pos_count_body = htmldoc.getElementsByTagName("p").length-1;
                    for(var l=0; l<htmldoc.getElementsByTagName("p").length-1; l++){
                       pos_links_info["body"+" "+sl_no+" "+l] =  htmldoc.getElementsByTagName("p")[l].innerText.trim();
                    }
                }
           })
        }
     )
     .catch(function(err) {
         console.log('Fetch Error :-S', err);
     });

     sl_no += 1;  
 }

问题原因

核心是异步操作的闭包陷阱:Fetch是异步任务,then回调函数会在循环结束后才执行,此时sl_no已经完成了所有递增操作,所有回调都会共享同一个sl_no的最终值,导致数据被错误地存入了后续的键中。

修复方案

方案1:捕获当前迭代的索引

在每次循环中创建独立的变量保存当前的sl_no,避免回调共享同一个变量:

var sl_no = 0;
for(const [key,value] of Object.entries(pos_links_info)){
   // 保存当前迭代的索引,每个回调绑定独立的变量
   const currentSlNo = sl_no;

   fetch('https://api.codetabs.com/v1/proxy?quest='+value)
      .then(function (response) {
         if (response.status !== 200) {
             console.log('Unable to fetch data from url link: ' +response.status);
             return;
         }
         response.text().then(function(data) {
            var date = new Date();
            var parser = new DOMParser();
            var htmldoc = parser.parseFromString(data, "text/html");
            var title;
            if(htmldoc.getElementsByTagName("h1")[0] != null)
               title = htmldoc.getElementsByTagName("h1")[0].innerText;
            else
               title = htmldoc.getElementsByTagName("title")[0].innerText;
            if(title != ""){
               // 使用currentSlNo代替sl_no
               pos_links_info["title"+" "+currentSlNo] = title.trim();
            }
            // 优化判断:getElementsByTagName始终返回集合,判断length更准确
            if(htmldoc.getElementsByTagName("img").length > 0){
                for(var l=0; l<htmldoc.getElementsByTagName("img").length; l++){ 
                   if(htmldoc.getElementsByTagName("img")[l].src.includes(value.split("https://")[1].split("/")[0]) && 
                       htmldoc.getElementsByTagName("img")[l].src.includes(date.getFullYear().toString())){
                          pos_links_info["image"+" "+currentSlNo] = htmldoc.getElementsByTagName("img")[l].src.trim();
                          break;
                   }
                }
            }
            if(htmldoc.getElementsByTagName("p").length > 0){
                for(var l=0; l<htmldoc.getElementsByTagName("p").length-1; l++){
                   pos_links_info["body"+" "+currentSlNo+" "+l] =  htmldoc.getElementsByTagName("p")[l].innerText.trim();
                }
            }
       })
    })
    .catch(function(err) {
        console.log('Fetch Error :-S', err);
    });

    sl_no += 1;  
 }

方案2:使用async/await同步化异步操作

将循环改为异步循环,确保每次Fetch完成后再执行下一次迭代,彻底避免闭包问题:

var sl_no = 0;
// 自执行异步函数,支持await语法
(async function() {
  for(const [key,value] of Object.entries(pos_links_info)){
    try {
      // 等待Fetch请求完成
      const response = await fetch('https://api.codetabs.com/v1/proxy?quest='+value);
      if (response.status !== 200) {
        console.log('Unable to fetch data from url link: ' + response.status);
        sl_no += 1;
        continue;
      }
      // 等待获取文本数据
      const data = await response.text();
      const date = new Date();
      const parser = new DOMParser();
      const htmldoc = parser.parseFromString(data, "text/html");
      let title;
      if(htmldoc.getElementsByTagName("h1")[0] != null)
        title = htmldoc.getElementsByTagName("h1")[0].innerText;
      else
        title = htmldoc.getElementsByTagName("title")[0].innerText;
      if(title != ""){
        pos_links_info["title"+" "+sl_no] = title.trim();
      }
      if(htmldoc.getElementsByTagName("img").length > 0){
          for(var l=0; l<htmldoc.getElementsByTagName("img").length; l++){ 
             if(htmldoc.getElementsByTagName("img")[l].src.includes(value.split("https://")[1].split("/")[0]) && 
                 htmldoc.getElementsByTagName("img")[l].src.includes(date.getFullYear().toString())){
                    pos_links_info["image"+" "+sl_no] = htmldoc.getElementsByTagName("img")[l].src.trim();
                    break;
             }
          }
      }
      if(htmldoc.getElementsByTagName("p").length > 0){
          for(var l=0; l<htmldoc.getElementsByTagName("p").length-1; l++){
             pos_links_info["body"+" "+sl_no+" "+l] =  htmldoc.getElementsByTagName("p")[l].innerText.trim();
          }
      }
      // 只有当前URL处理完成后才递增索引
      sl_no += 1;
    } catch(err) {
      console.log('Fetch Error :-S', err);
      sl_no += 1;
    }
  }
})();

额外优化点

  • 原代码中htmldoc.getElementsByTagName("img") != null的判断冗余,getElementsByTagName始终返回HTMLCollection对象,即使没有匹配元素也是空集合,改为判断length > 0更准确。
  • 嵌套的response.text().then()可以改为链式调用,方案2中用await实现了更整洁的写法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 12:42:24