-1

I'm aware that there are hundreds of thousands of threads on this subject already out there and I've been studying the problem for a few days now, but I still don't understand how to apply the examples that I've gone through to my case.

I start with an API call like this:

const getReleaseUrl = (discogsId) => { 
  db.getRelease(discogsId, (err, data) => {
    return data.uri
  }) 
}

console.log(getReleaseUrl("53130"));
// => undefined

How can I make sure that my app receives the data first, prevents the undefined return? Do I need to somehow make sure that db gets defined and connected to the remote database with my client id first? Do I need to write a function that somehow catches the undefined result and keeps running the API call over and over until the result is something different? Setting a timeout does not seem like a good permanent solution. I tried writing a Promise chain like this and got the TypeError: db.getRelease(...).then is not a function error:

const getReleaseUrl = (discogsId) => { 
  db.getRelease(discogsId, (err, data) => {
    return data
  })
  .then((data) => {
    return data.uri
  })   
}

console.log(getReleaseUrl("53130"));
sivanes
  • 713
  • 1
  • 13
  • 22
  • 1
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Brahma Dev Apr 10 '18 at 21:26
  • 2
    Regardless weather you end up using callbacks or promises or async/await, please read this answer to a related question to understand what's going on: https://stackoverflow.com/questions/17808651/return-function-javascript/17810720#17810720 – slebetman Apr 10 '18 at 21:40
  • @BrahmaDev that thread is a great resource and I have seen it before starting this thread, but the examples were a little too broad for me to come up with own solution – sivanes Apr 11 '18 at 20:11

5 Answers5

2

The way you have this coded, there is no possible way that your console message will ever return anything but undefined.

For starters your method does not have a return statement. Sure, the callback has a return statement, but that doesn't matter. No return statement, nothing to log. With the method you have defined, the best you can do is place a console message in the callback

const getReleaseUrl = (discogsId) => { 
  db.getRelease(discogsId, (err, data) => {
    console.log(data.uri);
    return data.uri
  }) 
}

getReleaseUrl("53130");

A better alternative would be to rewrite your function to accept a callback as a parameter:

const getReleaseUrl = (discogsId, callback) => { 
  db.getRelease(discogsId, callback);
}

getRelaseUrl("53130", (err, data) => {
  console.log(data.uri);
});

Here you are moving the logic about what to do with the returned data out of the main function, which gives you more flexibility. It also ensures that the log message will not be called until the data is ready.

Pop-A-Stash
  • 6,572
  • 5
  • 28
  • 54
  • Ok, but eventually this logic will go into a route definition and it needs to just return the value, no console logging. I know the code in OP ends with console log, but that was more to see what the code outputs. `console.log(getReleaseUrl('53130', (err, data) => { return data.uri }))` gives undefined once again – sivanes Apr 11 '18 at 19:55
  • 1
    Of course it returns undefined. You have to understand that async functions do not work the same way as sync functions. They don't return direct values. You have to use either this callback pattern or a promise. Either way you will be defining a function that will get called at a later time. There is no way around that. – Pop-A-Stash Apr 11 '18 at 21:07
  • Ok, I understand now, but it wasn't explained in your main response – sivanes Apr 11 '18 at 21:59
1

See the return added before db.getRelease:

Use node.js util to promisify db.getRelease

var util = require('util');
var getRelease = util.promisify(db.getRelease);

Return whole data:

const getReleaseUrl = (discogsId) => {       
  return getRelease(discogsId); 
}

OR return specific one:

const getReleaseUrl = (discogsId) => {       
      return getRelease(discogsId)
             .then(function(data){ 
                  return data.uri;
             }); 
}        
Zeeshan Hassan Memon
  • 8,105
  • 4
  • 43
  • 57
  • that returns my Discogs client data, instead of music release data for the `discogsId` – sivanes Apr 10 '18 at 21:21
  • yes --it will return promise on which you can call .then(function(data){// access data here }) – Zeeshan Hassan Memon Apr 10 '18 at 21:23
  • I had to uncheck your answer, because it doesn't work all the way through for me, after all. I have `var apiGetCall = util.promisify(db.getRelease(discogsId))` and `return apiGetCall` giving the `The "original" argument must be of type Function` error. – sivanes Apr 17 '18 at 21:24
1

You can either pass the callback through or use a Promise. Let's look at the promise option:

const getReleaseUrl = (discogsId) => {
  return new Promise((resolve, reject) => {
    db.getRelease(discogsId, (err, data) => {
      if (err) {
        reject(err);
      }

      resolve(data);
    });
  });
};

getReleaseUrl("53130")
  .then(console.log);
  .catch(console.error);

The issue is that, as you said, the call to db.getRelease is asynchronous, therefore getReleaseUrl will not return anything. Look at Pop-A-Stash's answer for a callback option.

0

It looks like db.getRelease already accepts a callback so you can handle success and errors there.

Your getReleaseUrl function doesnt return anything. Either remove the curly braces from the arrow function or add a return statement.

gillyhl
  • 475
  • 2
  • 7
0

use promises

var promise1 = new Promise(function(resolve, reject) {
resolve('Success!');
});

promise1.then(function(value) {
console.log(value);
// expected output: "Success!"
});

follow the steps here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

Acetech
  • 398
  • 4
  • 16