简体   繁体   中英

Promise.then() doesn't work on first button click

The problem I have is that when I click on the "NewQuoteButton" on the FIRST time the page loads, I get an error: quoteGenerator.getQuote(...).then is not a function . However, if I click the button again, I'm able to generate the quotes normally. Also upon examining my network tab in Chrome inspector, I noticed I had a lot more get requests and responses from the API while my quote that is being displayed is lagging behind.

I'm new to Promises and currently practicing utilizing it along with revealing module pattern. My understanding is that a Promise calls then() which takes a function with that resolved promise's value as the argument. Therefore, my getQuote() should work because it returns a Promise containing a quote object. Can someone point me in the right direction. Thanks!

const quoteGenerator = (function(){
  let url = "https://andruxnet-random-famous-quotes.p.mashape.com/?cat=famous";
  let apiHeader = new Headers({
    "X-Mashape-Key": "..."
  });
  let init = {
    headers : apiHeader,
  }
  let quoteInfo = {};

  let getQuote = function(){
    fetch(url, init)
      .then(response => {
        if (response.ok){
          return response.json();
        }
        throw new Error("Get request failed");
        }, networkError => console.log("Error: " + networkError))
      .then(jsonResponse => {
        quoteInfo = Promise.resolve({
          quote: jsonResponse.quote,
          author: jsonResponse.author
        }); 
       });
      return quoteInfo;   
  };


  return {
    getQuote : getQuote
  };

})();

//Triggers when user clicks on social media button to share quote.
//Checks to see if quote has been generated from quoteGenerator before
//sharing it, if not, then button does nothing.

const clickHandler = (function(){
  let triggerClicked = function(){
    $("#twitter").on("click", function(e){
      e.preventDefault();
      window.open("https://twitter.com/intent/tweet?text=asdf");
    });

    $("#newQuoteButton").on("click", function(e){
        //ERROR BELOW//
        quoteGenerator.getQuote().then(function(quoteInfo){
        //ERROR ABOVE//
        $("#quoteSection > h2").html("<i class='fa fa-quote-left'></i>" + 
                                     quoteInfo.quote + "<i class='fa fa-quote-right'></i>");
        $("#author").html("<i>- " + quoteInfo.author + "</i>");
      }).catch(function(e){
        console.log(e);
      });
    });
  };
  return {
    handleClicks: triggerClicked
  }
})();

clickHandler.handleClicks();

Return the data within Promise chain, and as pointed out by @nnnnnn

getQuote() returns quoteInfo . The first time it is called the promise has not yet completed, so quoteInfo is still the empty {} object it was initialised as.

quoteInfo can be omitted from the code

let getQuote = function() {
    return fetch(url, init)
      .then(response => {
        if (response.ok){
          return response.json();
        }
        throw new Error("Get request failed");
        }, networkError => console.log("Error: " + networkError))
      .then(jsonResponse => {
        return {
          quote: jsonResponse.quote,
          author: jsonResponse.author
        }; 
       });  
  };

  $("#newQuoteButton").on("click", function(e){
    //ERROR BELOW//
    quoteGenerator.getQuote().then(function(quoteInfo){
    //ERROR ABOVE//
    $("#quoteSection > h2")
    .html("<i class='fa fa-quote-left'></i>" 
    + quoteInfo.quote + "<i class='fa fa-quote-right'></i>");
    $("#author").html("<i>- " + quoteInfo.author + "</i>");
    }).catch(function(e){
      console.log(e);
    });
  });

You need to return the fetch call

let getQuote = function(){
    return fetch(url, init)   // <<<<<<< use 'return'
      .then(resp => {
       return resp.ok ? resp.json() : Promise.reject(Error('Get request failed'));
      })
      .catch(networkError => {
       console.error('Error: ' + networkError);
       return Promise.reject(networkError); // <<<< have to call reject here, or throw
      })
      .then(jsonResponse => {
        return {
          quote: jsonResponse.quote,
          author: jsonResponse.author
        }; 
      });
  };

dynamic typing FTW :)

4 tips:

  1. use console.error instead of console.log for errors since this writes to stderr not stdout

  2. use single quotes - they are easier to read IMO

  3. Promise catch blocks are usually a little cleaner than using "err-backs", IMO

  4. Use a real Error object for your errors, not just a message, otherwise will be difficult to track down. You don't need the new keyword, just call Error().

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM