Escher
Escher

Reputation: 5795

Chained promises give undefined function error, but I can execute the functions separately

I'm trying to chain some API calls before setting the textContent of some spans in my webpage. I can execute the following ajax API calls separately by pasting them into the console, but when I chain them as promises I get getFirstData() is undefined.

var first_data = [],
    second_data = [];

function getFirstData(){
    var xhr = new XMLHttpRequest();
    var url = "/API/first-data?format=json"
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && xhr.status == 200) {
            first_data = JSON.parse(xhr.responseText);
            return Promise.resolve('1');
        }
    }
    xhr.open("GET", url, true);
    xhr.send();
}

/*getSecondData is the same, but with a different API url. I'll DRY these 
two into one function that takes a url argument when I get it working.*/

getFirstData().then(getSecondData).then(createPage);

This is between <script> tags just before </body>. So what's wrong with the call to getFirstData() on the last line that causes the interpreter to say it's undefined? For reference, in the network log, getSecondData() is sent and returns just fine.

(Note: I'm specifically trying to do this without JQuery).

Upvotes: 0

Views: 116

Answers (2)

jonny
jonny

Reputation: 3098

The issue occurs because your function is returning undefined (in other words, getting to the end of the function block before it returns) before it ever gets a chance to return Promise.resolve('1').

Your function has to immediately return a Promise object, which becomes pending before eventually resolving inside your AJAX handler.

I'd also add error handling using the provided reject argument, as is standard for Promise objects.

function getFirstData(){
    return new Promise(function(resolve, reject) { // encapsulate code in a promise which returns immediately
      var xhr = new XMLHttpRequest();
      var url = "/echo/json"
      xhr.onreadystatechange = function() {
          if (xhr.readyState == 4 && xhr.status == 200) {
              first_data = JSON.parse(xhr.responseText);
              return resolve('1');
          }
          else {
            return reject('There was an error!') // reject the promise if error occurs
          } 
      }
      xhr.open("GET", url, true);
      xhr.send();
    });
}

And then catch it in the thenable chain:

getFirstData()
.then(getSecondData)
.catch(function(err){Throw err}) // catch the error if it throws
.then(createPage);

See working jsfiddle

Upvotes: 3

Victory
Victory

Reputation: 5890

getFirstData isn't returning a promise it returns undefined, which is not thenable.

function getFirstData(){
    return new Promise(function(resolve) {
      var xhr = new XMLHttpRequest();
      var url = "/API/first-data?format=json"
      xhr.onreadystatechange = function() {
          if (xhr.readyState == 4 && xhr.status == 200) {
              first_data = JSON.parse(xhr.responseText);
              resolve('1');
          }
      }
      xhr.open("GET", url, true);
      xhr.send();
    });
}

Upvotes: 3

Related Questions