1

I'm a new to coding and currently struggling with promises and await/async. I'm trying to access the data coming from the currPrice variable outside of the function scope. Can someone point me in the right direction?

        var price
        const getPrice = async () => {
           
            //Get the curent market price
            var marketSymobl = checkOrders[i].symbol.split(" - ")[0].toUpperCase()
            console.log(checkOrders[i].symbol)
           finnhubClient.quote(marketSymobl, (error, data, response) => {  
            const currPrice = data.c
             console.log(currPrice);
             return currPrice             
                });
        
            }
   
       //const x = getPrice().then(price => (console.log(x)))
          
         price = await getPrice()
         console.log(price);
Apoorva Chikara
  • 8,277
  • 3
  • 20
  • 35
Calin89
  • 47
  • 5
  • Does this answer your question? [NodeJS 7: EventEmitter + await/async](https://stackoverflow.com/questions/45967019/nodejs-7-eventemitter-await-async) – O. Jones Nov 11 '21 at 14:21

2 Answers2

1

The problem here is that when you do return currPrice you are returning from the callback in the quote function, thus not getting the value in the scope of the getPrice function. Because this is a callback, once it is executed the getPrice function already left the event stack with undefined as the return value. Check this video to learn about the event loop.

A way you could fix this would be to do something like:

const getPrice = async () => {
  //Get the curent market price
  const marketSymobl = checkOrders[i].symbol.split(" - ")[0].toUpperCase()     
  
  // We are wrapping this function in a promise so that we can await it
  const quotePromise = new Promise((resolve, reject) => finnhubClient.quote(marketSymobl, (error, data, response) => {
    if (error) {
       // Rejecting with error so you have the freedom to handle it however you want
       reject(error)
    } else {
       resolve(data.c)
    }
  });
  
  const currPrice = await quotePromise();

  return currPrice
}

This way you are returning from the getPrice function.

Note: I changed your var variables to let. Have a look at this post for an explanation why

Edit 1: I changed your marketSymobl variable to be const, since you don't mutate it afterwards

Edit 2: Wrapped the quote function in a promise so it can be awaited

Edit 3: Edited initial statement and added reference to event loop video.

Pedro Filipe
  • 995
  • 1
  • 8
  • 17
  • Hi Pedro. Thank you so much for you input. Unfortunately, anything that's outside of the quote function comes back undefined. I seem to be running out ideas. – Calin89 Nov 11 '21 at 14:53
  • What's the return type of the quote function? It might be a promise that you need to await for – Pedro Filipe Nov 11 '21 at 14:54
  • @PedroFilipe the above method will return null as the `cb` will be executed later in the execution in the event loop. It will always retun `null`. You need to make a change in the above function using await. – Apoorva Chikara Nov 11 '21 at 15:00
  • I hadn't notice that the getPrice function was already an async function X) – Pedro Filipe Nov 11 '21 at 15:05
  • I've managed to get it working with Pedro's code. Thank you very much for your input @PedroFilipe. NOTE: I've had to change const currPrice = await quotePromise() to const currPrice = await quotePromise as I was getting a TypeError: quotePromise() is not a function – Calin89 Nov 11 '21 at 15:47
  • Although, I have just one question. If finnhubClient.quote function is not promised based then what is it? – Calin89 Nov 11 '21 at 15:50
  • It returns callback and in callback it returns the resukt or error. – Apoorva Chikara Nov 11 '21 at 18:21
1

Async/await is just a syntactic sugar over promises. Promise can be returned as resolved or rejected and you use then/catch block to get the result/error based on the promise status returned. I am attaching a link to go through and understand how they work and when they are needed.

Now, let's understand the code you wrote:

        var price;

        // async method automatically returns promise
        const getPrice = async () => {
           
            //Get the curent market price
            var marketSymobl = checkOrders[i].symbol.split(" - ")[0].toUpperCase()
            console.log(checkOrders[i].symbol);

           // callback which is called when the async
           // task is completed or failed with error, so using await we can
           // make it await for this async task to complete first 
           // and then move to subsequent lines
           // try to put await code under try/catch

           try {
           const currPrice = await finnhubClient.quote(marketSymobl) // if this return the promise
            return currPrice.c
            } catch(e) {
              console.log(e);
            }
         }
        // here x will never get the value returned from getPrice. 
        // to get the values form getPrice, you can use then operator
        // on x like this
        //const x = getPrice();
        // x.then(price => (console.log(x))).catch((e) => console.log(e));

      
       //const x = getPrice().then(price => (console.log(x)))
          
         price = await getPrice(); // this will return the response or error based on promise value
         console.log(price);

Note: finnhubClient.quote if this method returns the promise the above code will work fine else it won't work. Also, I suggest you to check this link for async/await.

When finnhubClient.quote is not promise based, you can return promise.

const getPrice = () => {
           
     return new Promise((res, rej) => {
         //Get the curent market price
            var marketSymobl = checkOrders[i].symbol.split(" - ")[0].toUpperCase()
            console.log(checkOrders[i].symbol)
           finnhubClient.quote(marketSymobl, (error, data, response) => {       
            if (error) reject(error);
            const currPrice = data.c
             console.log(currPrice);
             resolve(currPrice)            
            });
        
     })
            
}
Apoorva Chikara
  • 8,277
  • 3
  • 20
  • 35
  • You can only await the quote function if it returns a promise, otherwise it won't do anything, you will need to wrap that function in a promise – Pedro Filipe Nov 11 '21 at 15:09
  • Hi Apoorva, Thank you very much for your explanation. I've tried adding await to the quote function. However I'm getting this error: Warning: superagent request was sent twice, because both .end() and .then() were called. Never call .end() if you use promises Error: .end() was called twice. This is not supported in superagent – Calin89 Nov 11 '21 at 15:14
  • Check out my edited answer to see if it solves your problem – Pedro Filipe Nov 11 '21 at 15:15
  • @PedroFilipe thats why I have added a note for that specific function. If SO can add the implementation of that function I can make it promise based. – Apoorva Chikara Nov 11 '21 at 15:20
  • @Calin89, you need to make `finnhubClient.quote` promise based or you can share the definition. I can help with that. – Apoorva Chikara Nov 11 '21 at 15:21
  • From the name of the function I inferred @Calin89 is using https://github.com/Finnhub-Stock-API/finnhub-js, so I went to check their API, which is not promise based – Pedro Filipe Nov 11 '21 at 15:23
  • @PedroFilipe If it's not promised based then how do I solve this problem? I've tried your edited code and getting. const currPrice = await quotePromise(); ^ TypeError: quotePromise is not a function – Calin89 Nov 11 '21 at 15:29
  • I apologies in advance for this newbie questions. I've tried for almost 10hrs to solve this and no luck so far. I've only started coding a month ago. – Calin89 Nov 11 '21 at 15:30
  • Thats fine. Things happen. Let me update the answer and check that. – Apoorva Chikara Nov 11 '21 at 15:34
  • Just updated the answer, whenever you have a method which is not promise based you can convert that with this format and you are good to go. – Apoorva Chikara Nov 11 '21 at 15:58