5

I am trying to access the value in Promise outside the then function using JavaScript. I am new to Promise and I not sure if I am doing it correctly. Here is my code:

//removed code

Basically I have store the value returned by Promise into a separate variable inside the then function. I wanted to access them at the later part, but when I print them out at later part, it returns me undefined. Any ideas on how to resolve this?

I have found examples and followed them but it does not work. I not sure which part of my code was wrong. I opened a thread earlier on and it was marked as duplicate and closed. I guess my inquiry wasn't clear enough, therefore I am rephrasing the question here.

ouflak
  • 2,458
  • 10
  • 44
  • 49
  • 1
    You are trying to log p1 and p2 directly after creating the promises. Promises are asynchronous, meaning those values won't be set until after the promise resolves, which is why you're getting undefined. Also, you are doing promise.all on the values the promises resolve with, not the promise themselves. This won't work. If you switch out your Promise.all p1, p2 values with promiseStoreKey and promiseCurrencyKey you might have better luck. – jas7457 Jul 21 '17 at 02:12
  • 5
    There are two kinds of javascript programmers: ...and those who don't. Those who understand asynchronous code... – Gerardo Furtado Jul 21 '17 at 02:12
  • @jas7457 Thanks it works by changing p1 and p2 to the key you mentioned! –  Jul 21 '17 at 02:21

3 Answers3

3

Here is a working promise all example I've posted recently. Promise skeleton

It is in ES6, so replace

return new Promise((y,n) => setTimeout( () =>  y('secondCall'), 800 ));

with

return new Promise( function(resolve,reject) {
//... here you call your firebase and resolve(returnValue) once you get the result
});

in your case

var currencyKey;
return new Promise( function(resolve,reject) {
       firebase.database().ref('currencies').orderByChild('currencySymbol').equalTo('$').once('value', function(snapshot) {
                var currencyData = snapshot.val();
                if (currencyData){
                  console.log('currency exists.');
                }else{
                    currencyKey = firebase.database().ref('currencies').push({
                    currencyName : "Singapore Dollar",
                    currencySymbol : "$"
                  }).getKey();

                  resolve(currencyKey);
                }
              });
        });
});

Notice, I've added var currencyKey; above the promise. This makes currencyKey accessible outside of the Promise block of code.

Jarek Kulikowski
  • 1,399
  • 8
  • 9
  • But how do I access it in the later part? Just by calling currencyKey? –  Jul 21 '17 at 03:54
  • @EmmaHannah I've modified the code (above) and put var currencyKey above the Promise. This makes it accessible to the rest of the code. You had a scoping problem. This should fix it. – Jarek Kulikowski Jul 21 '17 at 15:24
2

In order to execute logic when both promises resolved their value you should use the Promise.all() on the promises objects.

  Promise.all([promiseCurrencyKey, promiseStoreKey]).then(values => { 
    console.log(values); 
  });

JavaScript is a single threaded language which means having the code waiting for something to happen will block everything else. Now, we can't really code without things that sometimes might take a while, and this is what Promises are for.

The logic in a promise is being processed in the background and not blocking the code. In order to use the value resolved by the promise you can set a callback function into the then() method which will be executing when the value is resolved.

Having the callback executing when the value resolved doesn't make the rest of the code to wait.

So describing what happen with your code is as follow:

  1. run some logic in the background that resolve value
  2. when value resolved set it to p1
  3. print p1 - the logc in step 1 didn't finish yet, meaning the logic in the then function also didn't happen and value still not set into p1

Same happen for p2.

Now, when you are using Promise.all() you are performing it on two simple empty variables and not on the promises objects so the result is array with two empty variables...

In the second example after the editing the problem is that you defined the promiseBranchKey inside the first promise function so it's exist only for the scope of this function and not out of it where you are calling Promise.all().

let promiseMerchantKey = new Promise((resolve, reject) => {
    firebase.database().ref('merchants').orderByChild('merchantName').equalTo('NTUC').once('value', function(snapshot) {
        let merchantData = snapshot.val();
        if (merchantData){
            console.log('merchant exists');
            resolve(merchantData.key);
        }else{
            // get merchant unique push ID
            let merchantKey = firebase.database().ref('merchants').push({
                address : "NTUC"
            }).getKey();
            resolve(merchantKey);
        }
    });
});

let promiseBranchKey = new Promise((resolve, reject) => {
    firebase.database().ref('merchants').orderByChild('branchAddress').equalTo('Blk 167').once('value', function(snapshot) {
        let branchData = snapshot.val();
        if (branchData){
            console.log('branch exists');
            resolve(branchData.key);
        }else{
            // get branch unique push ID
            promiseMerchantKey.then((merchantKey) => {
                let branchKey = firebase.database().ref('merchants').child(merchantKey).push({
                    branchName : 'Marsiling',
                    branchAddress : 'Blk 167'
                }).getKey();
                resolve(branchKey);
            });
        }
    });
});

promiseBranchKey.then((branchKey) => { 
    console.log('branchkey' + branchKey); 
    console.log('currencykey' + promiseMerchantKey);
});

Considering that promise 2 depends on the value resolved by promise 1 you should have the then() method used inside it and causing the resolve of promise 2 to happen only when the first promise already returned value.

Because resolving of promise 2 can be done only if promise 1 is already resolved there is no need in promise.all() and we can use then on promise 2 to indicate when both of them were processed.

Ori Shalom
  • 470
  • 4
  • 11
  • Can you help me take a look at the EDIT portion? The error that I am currently having is 'Can't find variable: promiseBranchKey' –  Jul 21 '17 at 03:21
  • You defined the promiseBranchKey inside the first promise function so it's exist only for the scope of this function and not out of it where you are calling `Promise.all()`. – Ori Shalom Jul 21 '17 at 03:42
  • Any ideas on how to resolve this? –  Jul 21 '17 at 03:51
1

One approach would be to utilize async/await

async function getData() {
  const p1 = await new Promise(resolve => 
               setTimeout(resolve, Math.floor(Math.random() * 1200), "p1"));
  console.log(p1);
  const p2 = await new Promise(resolve => 
               setTimeout(resolve, Math.floor(Math.random() * 1500), "p2"));
  console.log(p2);
  // do stuff
  // access `p1`, `p2` "later"
  const p3 = await Promise.all([p1, p2])
             .then(results => 
                new Promise(resolve => 
                  setTimeout(resolve, 1800, [...results, "p3"]))
             );
             
  console.log(p3);
  
  return [...p3, "p4"]
}

getData()
.then(res => console.log(res))

Alternatively, simply chain .then() or .catch() calls instead of nesting Promise calls; though there should not be an issue with nesting Promise calls of .then() either.

The important point is determining what the expected result is at a given point in the procedure that you are performing.

guest271314
  • 1
  • 15
  • 104
  • 177
  • @EmmaHannah See [You're Missing the Point of Promises](https://gist.github.com/domenic/3889970#youre-missing-the-point-of-promises) – guest271314 Jul 21 '17 at 02:29
  • Hey can you check my edited portion? I have got it working. But lets says my branch required the promise from my merchant, I nested it inside, but then the bottom Promise.all is telling me an error message saying promiseBranchKey not found. Any idea how to rectify this? –  Jul 21 '17 at 02:47
  • Are you receiving an error? Where? No `.catch()` or second parameter to `.then()` appears at JavaScript at Question. The issue is that you do not `return` a value from `.then()`, `return promiseBranchKey` from `.then()` chained to `promiseMerchantKey`, `promiseMerchantKey.then((merchantKey) => { let promiseBranchKey = new Promise((resolve, reject) => {}); return promiseMerchantKey})`. See [Why is value undefined at .then() chained to Promise?](https://stackoverflow.com/q/44439596/2801559) – guest271314 Jul 21 '17 at 02:55
  • Yeah the error tells me cannot find variable promiseBranchKey, I guess it's because it's nested inside the then function. Sorry but I am confused, so what should I change if that's the case? It is no longer undefined for the promises as I tested other promises using the .all, it actually printed out the values. –  Jul 21 '17 at 02:59
  • What do you mean by "cannot find variable promisesBranchKey" as an error? What is the issue if you `return` a value from `.then()`? _"so what should I change if that's the case?"_ Have you read code and link at previous comment? – guest271314 Jul 21 '17 at 03:03
  • I made some changes in the EDIT portion in my question. Currently when I print out promiseMerchantKey and promiseCurrencyKey, it did print out the value. However, not for promiseBranchKey. Could that be a reason because I am declaring promiseBranchKey inside the then function of promiseMerchantKey? The error message just says 'Can't find variable: PromiseBranchKey' –  Jul 21 '17 at 03:06
  • Yeah I did but couple days ago, I am using the await and async. However, there are some errors stating that await is a reserved keyword. Therefore, I found an alternative as my code in the EDIT portion. It's so far working as I managed to retrieve the value but not the case like for instance, I got promise1 and promise2. Promise2 needs the value from promise1, therefore I nested the promise2 inside the then function of promise1. When I tried to print out at the end, I got an error message saying 'Cant find variable: promise2' because I declared promise2 inside the then function of promise1. –  Jul 21 '17 at 03:09
  • Why is `Promise.all()` necessary? You can chain `.then()` if you are only expecting one value. Though you still need to return a value from `.then()`. Where is `promiseCurrencyKey` defined? – guest271314 Jul 21 '17 at 03:16
  • Omg, sorry I am lost. I declared the promiseCurrencyKey same as the promiseMerchantKey. If they are declared in that way, they are accessible. The problem that I am currenly having is the promiseBranchKey which I declared under the then function of promiseMerchantKey because I needed the value returned to insert a new record into firebase. Any suggestion on what should I do now? I am seriosuly lost, sorry about that! –  Jul 21 '17 at 03:27
  • You mentioned that you read the previous comments, yes? You need to `return` the `Promise` or value from `.then()`. You should be able to accomplish what you appear to be attempting to achieve with a number of patterns `promiseCurrencyKey = promiseMerchantKey.then((merchantKey) => return new Promise((resolve, reject) => {})) // see return statement promiseCurrencyKey.then(res => console.log(res))` – guest271314 Jul 21 '17 at 03:34
  • 1
    I see I see. Thanks so much for all the helps!!! –  Jul 21 '17 at 04:17