0

I use promise to handle async method getPropoalByPeriod but I can't get the obj data out of the foo function where console.log(89,foo(mycallback())) prints undefined. Please help to get data outside so that I can process them.

function foo(callback) {
    var MyPromises = periods.map((p) => {
        return new Promise((resolve, reject) => {
            myFunctions.getPropoalByPeriod(p.id, localStart, function(err, rows) {
                if (err) {
                    console.log(62, err);
                    return reject(err);
                } else
                    var obj = {
                        id: p.id,
                        name: p.name,
                        savedServices: rows[0] ? rows[0].services : '',
                    };
                resolve(obj)
            })
        })
    })
    Promise.all(MyPromises)
    .then((p) => {
        callback(p)
    })
    .catch((err) => console.log(79, err));
}

function mycallback(res) {
    console.log(85, res)
    return res
}
foo(mycallback)
console.log(89, foo(mycallback()))

This issue is different from what is on SO. It is using map() where I couldn't figure out how to apply await compared to the straightforward example of promise or async/await.

Abdulrahman Alsari
  • 1,667
  • 4
  • 17
  • 28
  • 2
    Don't use a callback. Just return the promise. Then call it as `foo().then(mycallback)` – Bergi Mar 21 '18 at 22:53

1 Answers1

1

then accepts a function and send the result of the Promise to that function (the first argument of your callback function).

Promises are asynchronous by nature, and their output is not immediately available. That's why we use then method to get the result.

In your code, mycallback receives the result of all the Promises, and the value res (that is printed along with value 85) is what you get from the promises.

Remember, that because you used Promise.all on an array of promises, you get an array of results from all the succeeded promises. This means that res is an array, not a single value.

Your code suffers from some other problems. The first call to your foo method is good, and correctly prints the results. Your second call however (console.log(89,foo(mycallback()))) is incorrect, because instead of passing a function to foo, you are passing the result of calling callback(), which is undefined, to the foo function. Remove that line, and everything should be working.

Here is the complete revised code:

function foo() {
  var MyPromises = periods.map((p) => {
    return new Promise((resolve, reject) => {
      myFunctions.getPropoalByPeriod(p.id, localStart, function(err, rows) {
        if (err) {
          console.log(62, err);
          reject(err);
        } else {
          var obj = {
            id: p.id,
            name: p.name,
            savedServices: rows[0] ? rows[0].services : '',
          };
          resolve(obj);
        }
      });
    });
  });
  return Promise.all(MyPromises);
}

// Pure promises:

foo().then(r => console.log(r));

// or using async/await:

async function run() {
  console.log(await foo());
}

run();

// 

Remember, the point of using promises is freeing you from using callbacks. So there is no need to pass a callback to foo.

You can use .then or await to retrieve the value of a Promise. You can only use await in an async function; so you cannot use await in global score, and you have to define a function, and call it. So in your case, using .then is easier.

Mohammad Dehghan
  • 17,853
  • 3
  • 55
  • 72
  • thank you very much for the explanation. But would you help in providing a mechanism to get the data outside? at the place of `console.log(89,foo(mycallback()))` ? – Abdulrahman Alsari Mar 21 '18 at 10:49
  • 2
    That's not possible. There is absolutely *no way* to get the result of a promise *synchronously*. To make your code more readable and easier to maintain, you can use async/await feature of ES2017/TypeScript. – Mohammad Dehghan Mar 21 '18 at 10:51
  • you said `This means that res is an array, not a single value.` , but console at least shouldn't say undefined, it will say array, right? – Abdulrahman Alsari Mar 21 '18 at 10:51
  • `but it is safer to put them in place` How is that? If you know how ASI works, there's no issue. It's not "unsafe", it's just different than your personal coding style preference. – Patrick Roberts Mar 21 '18 at 10:52
  • @MohammadDehghan I tried `async/wait` and no benefit, would you mind if you apply it and update your answer? I will appreciate that and mark it as best answer if it works. thank yoou. – Abdulrahman Alsari Mar 21 '18 at 10:54
  • @PatrickRoberts There *are* situations where forgetting semicolons results in undesired behavior. This code is safe, but it is a good practice to put the semicolons after every statement. It is just safer. – Mohammad Dehghan Mar 21 '18 at 11:05
  • @MohammadDehghan [No it's not.](https://standardjs.com/) Failing to learn the _very few, very specific_ situations where adding semicolons is actually necessary at all, is what causes "undesired behavior". It has nothing to do with "safety", and any static analysis tool / linter worth its salt can easily catch such operator errors. – Patrick Roberts Mar 21 '18 at 11:28
  • @PatrickRoberts OK. It seem you're more right than me! But I am in habit of adding them because I am a C family developer. – Mohammad Dehghan Mar 21 '18 at 12:00
  • @PatrickRoberts ... and I totally dislike staring some lines with a semicolon! – Mohammad Dehghan Mar 21 '18 at 18:19
  • @MohammadDehghan sure but the point of that requirement is to discourage you from writing lines that start with a `(`, `[`, or prefixed unary operator in the first place. The only fairly common case I encounter the need is ES6-style variable swap, e.g. `;[a, b] = [b, a]` – Patrick Roberts Mar 21 '18 at 19:44
  • thank you for the code, I will try it and give feedback. – Abdulrahman Alsari Mar 22 '18 at 08:07
  • thank you Mr. @MohammadDehghan, you did a great job, it worked, it is the best answer! – Abdulrahman Alsari Mar 22 '18 at 11:34
  • @PatrickRoberts I explained how this post is different. Please check the post. And would you help me to make it different so to remove the duplicate in the title? maybe the title should be updated. – Abdulrahman Alsari Mar 22 '18 at 11:40
  • @MohammadDehghan may you upvote the post please? – Abdulrahman Alsari Mar 22 '18 at 13:36
  • @Diamond No. Sorry. StackOverflow doesn't work like this. Votes are earned through good posts. Your post (question) shows lack of basic knowledge about the concept you're asking about. – Mohammad Dehghan Mar 22 '18 at 15:52
  • @Diamond I upvoted your post. But please do your homework before posting questions (study about the subject, and try to understand what's happening in the code). – Mohammad Dehghan Mar 24 '18 at 12:47
  • thank you, I actually spent days on that, yet the answer was easy but I have not come to it! Thank you again. – Abdulrahman Alsari Mar 24 '18 at 12:52