K.Jower
K.Jower

Reputation: 13

nothing resolve when try to fetch in promise nodejs

I use fetch to toggl api the code like this.

const getData = (workspaceId, start, end, projectId, page, tmpData) => {
  return new Promise((resolve,reject) => {
    let result = []
    if (tmpData !== null){
      result.push(tmpData)
    }
    fetch('https://toggl.com/reports/api/v2/details?workspace_id='+workspaceId
      +'&since='+start+'&until='+end+'&user_agent=api_test&project_ids='+projectId
      +'&page='+page, {
      method: 'get',
      headers: {
        'Authorization': 'Basic '+new Buffer(token.api_token+':api_token').toString('base64')
      }
    }).then(res=>{
      return res.json()
    }).then(json=>{
      if (json.data.length > 0){
        result.push(json)
        console.log('on page '+page)
        getData(workspaceId, start, end, projectId, page+1, result)
      } else {
        console.log('end on page '+page)
        result.push(json)
        console.log(result) //this line is log the result successfully
        resolve(result) // but this not resolve i don't know why
      }
    }).catch(err=>{
      reject(err)
    })
  })
}

the toggl api has rate limit to access if you want to get all data you need to increase the parameter (page) to access the next data.

it's not possible to get all data in once request.

Upvotes: 1

Views: 334

Answers (2)

danh
danh

Reputation: 62686

Late to the party, glad there's an excepted answer, but I'd fix the OP code by reorganizing a bit. It looks like the fetch() function answers a promise, so there's no need to create a Promise. Next, I'd split the fetching and the recursive call to it into separate methods. First...

// do a fetch, appending to result, answer a promise for the length of data fetched
const fetchAndAppend = (workspaceId, start, end, projectId, page, result) => {
    return fetch('https://toggl.com/reports/api/v2/details?workspace_id='+workspaceId
      +'&since='+start+'&until='+end+'&user_agent=api_test&project_ids='+projectId
      +'&page='+page, {
      method: 'get',
      headers: {
        'Authorization': 'Basic '+new Buffer(token.api_token+':api_token').toString('base64')
      }
    }).then(res=>{
        return res.json()  // is this really async?  I would have guessed not
    }).then(json=>{
        result.push(json)
        return json.data.length
    })
}

That's easy to read: fetch, append to a result, answer the length. Now make a recursive method that calls the fetch...

// keep fetching until no data is returned, answer a promise for the complete data
const getData = (workspaceId, start, end, projectId, page, tmpData, result) => {
    result = result || []
    return fetchAndAppend(workspaceId, start, end, projectId, page, result)
    .then(length => {
        return (length)? getData(workspaceId, start, end, projectId, page+1, tmpData, result) : result
    })
}

I think this works, and it has the benefit that the next reader can see that it works and can see why it works.

Upvotes: 0

Icepickle
Icepickle

Reputation: 12796

The problem in this case is your getData to call the second page. In case there would be no data, your resolve would work but now your problem is that you resolve, with nobody listening.

if (json.data.length > 0){
    result.push(json)
    console.log('on page '+page)
    // nobody is listening for the promise to complete
    // any resolve called later, will be not handled
    getData(workspaceId, start, end, projectId, page+1, result) 
}

You could change this in following way, which would then resolve your callback

getData(workspaceId, start, end, projectId, page + 1, result).then(() => resolve(result))

So essentially this is what happens:

  • Call to getData(A)
    • Call to getData(B)
    • Call to getData(C)
    • C resolves as there is no more data available

With the change on the line as suggested, the flow would be

  • Call to getData(A)
    • Call to getData(B)
    • Call to getData(C)
    • C resolves as there is no more data available
    • B resolves
  • A resolves

So you would essentially get 3 resolves

The full code change would then be:

const getData = (workspaceId, start, end, projectId, page, tmpData) => {
  return new Promise((resolve,reject) => {
    let result = [];
    if (tmpData !== null){
      result.push(tmpData);
    }
    fetch('https://toggl.com/reports/api/v2/details?workspace_id=' + workspaceId + '&since='+ start + '&until=' + end +  '&user_agent=api_test&project_ids=' + projectId +'&page=' + page, {
          method: 'get',
      headers: {
        'Authorization': 'Basic '+new Buffer(token.api_token+':api_token').toString('base64')
      }
    }).then(res=>{
      return res.json();
    }).then(json=>{
      if (json.data.length > 0){
        result.push(json);
        console.log('on page '+page);
        getData(workspaceId, start, end, projectId, page+1, result)
            .then(() => resolve(result))
            .catch(err => reject(err));
      } else {
        console.log('end on page '+page);
        result.push(json);
        console.log(result);
        resolve(result);
      }
    }).catch(err=>{
      reject(err);
    });
  });
};

Upvotes: 1

Related Questions