Promise.all解析得太早了

时间:2017-05-28 14:19:02

标签: javascript reactjs es6-promise

我正在加载存储在多个JSON中的论坛数据,以便在网站上显示,然后使用React进行渲染。数据本身分为两种类型的文件:线程数据和用户数据。

// threads/135.json
{
  "title": "Thread Title",
  "tid": 135,
  "posts": [
    {
      "pid": 1234,
      "timestamp": 1034546400,
      "body": "First forum post",
      "user": 5678
    },
    {
      "pid": 1235,
      "timestamp": 103454700,
      "body": "Reply to first forum post",
      "user": 9876
    }
  ]
}

加载线程数据后,将根据用户ID加载用户数据。

// user/1234.json
{
  "id": 1234,
  "name": "John Doe",
  "location": "USA"
}

实际代码基于Google's Introduction to Promises,变成了一个看起来像这样的函数:

export default function loadData(id) {

  var didLoadUser = [];
  var dataUsers = {};
  var dataThread = {};

  getJSON('./threads/' + id + '.json').then(function(thread) {
    dataThread = thread;

    // Take an array of promises and wait on them all
    return Promise.all(
      // Map our array of chapter urls to
      // an array of chapter json promises
      thread.posts.map(function(post) {
        if (post.user > 0 && didLoadUser.indexOf(post.user) === -1) {
          didLoadUser.push(post.user);
          return getJSON('./users/' + post.user + '.json') ;
        }
      })
    );
  }).then(function(users) {
    users.forEach(function(user) {
      if (typeof user !== 'undefined') {
        dataUsers[user.id] = user;
      }
    });
  }).catch(function(err) {
    // catch any error that happened so far
    console.error(err.message);
  }).then(function() {
    // use the data
  });
}

// unchanged from the mentioned Google article
function getJSON(url) {
  return get(url).then(JSON.parse);
}

// unchanged from the mentioned Google article
function get(url) {
  // Return a new promise.
  return new Promise(function(resolve, reject) {
    // Do the usual XHR stuff
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      // This is called even on 404 etc
      // so check the status
      if (req.status == 200) {
        // Resolve the promise with the response text
        resolve(req.response);
      }
      else {
        // Otherwise reject with the status text
        // which will hopefully be a meaningful error
        reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      reject(Error("Network Error"));
    };

    // Make the request
    req.send();
  });
}

在将代码转换为在我的组件的componentWillMount函数中调用的函数之前,代码本身工作正常。

componentWillMount: function() {
    this.setState({
      data: loadData(this.props.params.thread)
    })
}

我怀疑错误在于函数本身,因为在加载线程数据之后和加载任何用户数据之前看起来Promise.all会被解析。

我最近才开始使用promises,所以我的知识非常基础。我究竟做错了什么?我需要将main函数包装在另一个promise中吗?

1 个答案:

答案 0 :(得分:2)

this.setState({
   data: loadData(this.props.params.thread)
})

您无法从上面的异步调用返回,因为数据尚未以同步方式及时准备好。您返回承诺,因此代码应该如下所示:

loadData(this.props.params.thread)
 .then((data)=> {
   this.setState({
     data,
   })
})

注意: 为了使其正常工作,您需要返回来自loadData的承诺,因此:

...
return getJSON(..
...