javascript - 在 JavaScript 中使用 for...in 获取奇怪的东西

标签 javascript reactjs ecmascript-6 es6-promise

我正在尝试使用 fetch 从两个不同的 URL 请求 JSON 数据并在将其加载到状态对象之前使用 JSX 对其进行格式化。它仅从其中一个 URL 检索数据。

我的对象中有这样的 URL:

var urls = {
  'recent': 'https://fcctop100.herokuapp.com/api/fccusers/top/recent',
  'alltime': 'https://fcctop100.herokuapp.com/api/fccusers/top/alltime',
};

我想通过迭代包含 URL 的对象来获取数据,将其转换为 JSX 对象数组,然后将结果存储在我的主对象的状态中。

for (var key in urls) {
  var url = urls[key];
  fetch(url).then((response) => {
    response.json().then((json) => {
      var userNumber = 0;
      var html = json.map((user) => {
        return (
          <tr>
            <td>{++userNumber}</td>
            <td>
              <img src={user.img} alt={user.username} />
              {user.username}
            </td>
            <td>{user.recent}</td>
            <td>{user.alltime}</td>
          </tr>
        );
      }); // Close json.map block.
      this.setState({
        'data': { [key]: html }
      });
      console.log(key);
    }); // Close response.json block.
  }); // Close fetch block.
}

它应该从 recent 获取数据URL 并将格式化结果存储在 this.state.data.recent 中。然后它应该对 alltime 做同样的事情,将结果存储在this.state.data.alltime中。它实际上所做的似乎是 fetch alltime两次。

这对我来说完全没有意义。我什至不知道发生了什么。

我注意到,如果我将日志语句放入 fetch 的箭头函数中,它会显示它同时命中了两个键和两个 URL,但是如果我将日志语句放在 response.json() 的末尾,箭头函数,json.map之后,它显示alltime两次。

它正在处理第二个 URL 两次。我知道顺序不能保证,但它确实倾向于按照我定义它们的顺序处理它们,我认为这在这里很重要。即使这是同时发生的异步请求的问题,但它们是两个不同的请求,最终将数据存储在两个不同的位置,不是吗?

此代码的一个版本也是 at CodePen ,但当我试图弄清楚这里发生了什么时,我可能会改变它。

完整代码:

var urls = {
  'recent': 'https://fcctop100.herokuapp.com/api/fccusers/top/recent',
  'alltime': 'https://fcctop100.herokuapp.com/api/fccusers/top/alltime',
};

function View(props) {
  return (
    <form className="view">
      <label>View: </label>
      <label><input type="radio" name="view" value="recent" checked={props.view == 'recent'} />Past 30 Days</label>
      <label><input type="radio" name="view" value="alltime" checked={props.view == 'alltime'} />All Time</label>
    </form>
  );
}

class Main extends React.Component {
  constructor() {
    super();
    this.state = {
      'view': 'recent',
      'data': {
        'recent': null,
        'alltime': null,
      },
    };
  }
  
  componentWillMount() {
    for (var key in urls) {
      var url = urls[key];
      console.log(key);
      console.log(url);
      fetch(url).then((response) => {
        response.json().then((json) => {
          var userNumber = 0;
          var html = json.map((user) => {
            return (
              <tr>
                <td>{++userNumber}</td>
                <td>
                  <img src={user.img} alt={user.username} />
                  {user.username}
                </td>
                <td>{user.recent}</td>
                <td>{user.alltime}</td>
              </tr>
            );
          }); // Close json.map block.
          
          console.log('Setting state for ' + key);
          this.setState({
            'data': { [key]: html }
          });
        }); // Close response.json block.
      }); // Close fetch block.
    }
    /*
    fetch(urls.recent).then((response) => {
      response.json().then((json) => {
        var view = 'recent';
        var html = json.map((user) => {
          return (
            <tr>
              <td>
                <img src={user.img} alt={user.username} />
                {user.username}
              </td>
              <td>{user.recent}</td>
              <td>{user.alltime}</td>
            </tr>
          );
        }); // Close json.map block.
        this.setState({
          'data': { [view]: html },
        });
      }); // Close response.json block.
    }); // Close fetch block.
    */
  }
  
  render() {
    return (
      <div>
        <View view={this.state.view} />
        <table>
          {this.state.data[this.state.view]}
        </table>
      </div>
    );
  }
}

ReactDOM.render(<Main />, document.getElementById('app'));
img {
  width: 2em;
  height: 2em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<main id="app"></main>

最佳答案

异步函数和for循环是js中的大话题之一(可以在SO上搜索它们)。它们可以使用 IIFE 或 let 来解决:

for(let key in keys){
  //your code, key will be locally scoped
}

或者:

for(key in keys){
(function(key){
   //key is locally scoped
 })(key);
 }

会发生什么?你只有一个键,所以它一次只有一个值,for 循环将在异步函数执行之前完成:

key="recent";
key="alltime";
//async functions run

为什么本地( block )作用域有帮助?嗯:

 {
 key="recent";
 //async function one fires, key is recent
 }
 {
 key="alltime";
 //async function two fires, key is alltime
 }

关于javascript - 在 JavaScript 中使用 for...in 获取奇怪的东西,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41660830/

相关文章:

javascript - 使用 ES6 import 语句时,有没有办法防止未定义的项目?

javascript - 为什么 super() 应该在访问 this 之前出现

javascript - 在 Three.js 中使用点云创建平面网格

android - 在另一个 : React Native 之上叠加或添加 View

javascript - React-router - 通过 history.push() 导航刷新页面

javascript - 如何添加带圆圈的图像Reactjs

javascript - 相邻的 JSX 元素必须包裹在 React Native 中的封闭体中

javascript - 相对于浏览器窗口中的位置缩放文本。

c# - ajax在asp.net中的使用

javascript - 在向链上传递 promise 的同时更改数据结果?