How to extract data out of a Promise

86,082

Solution 1

NO you can't get the data synchronously out of a promise like you suggest in your example. The data must be used within a callback function. Alternatively in functional programming style the promise data could be map()ed over.

If your are OK using async/await (you should it's awesome) then you can write code that looks synchronous yet retain the asynchronicity of a promise (see @loganfsmyth comments).

const { foo, bar }  = await iAmAPromise.then(result => result.data);

Overall since you are already using ES6 I assume you are also using a transpiler. In which case you should definitely give async/await a try. Just be sure to weight in the decision that as today they are not yet a ratified specification.

Solution 2

While you can get a value from an awaited Promise inside an async function (simply because it pauses the function to await a result), you can't ever get a value directly out of a Promise and back into the same scope as the Promise itself.

That's because "out of" would mean trying to take something that exists in the future (the eventually resolved value) and putting it into a context (synchronous variable assignment) that already happened in the past.

That is, time-travel. And even if time-travel were possible, it probably wouldn't be a good coding practice because time travel can be very confusing.:)

In general, if you find yourself feeling like you need to do this, it's good sign that you need to refactor something. Note that what you're doing with "result => result.data" here:

Promise.then(result => result.data, errorHandler);
// rest of script

..is already a case of you working with (literally, mapping over) the value by passing it to a function. But, assuming that "// rest of script" does something important related to this value, you probably want to continue mapping over the now updated value with yet another function that then does something side-effect-y with the value (like display the data on the screen).

Promise
    .then(result => result.data)
    .then(data => doSomethingWithData)// rest of script
    .catch(errorHandler);

"doSomethingWithData" will be called (if it's ever called) at some unknown point in the future. Which is why it's a good practice to clearly encapsulate all that behavior into a specific function and then hook that function up to the Promise chain.

It's honestly better this way, because it requires you to clearly declare a particular sequence of events that will happen, explicitly separated out from the first run through all of your application code's execution.

To put it another way, imagine this scenario, hypothetically executed in the global, top-level scope:

const { foo, bar } = Promise.then(result => result.data, errorHandler);
console.log(foo);
//...more program

What would you expect to happen there? There are two possibilities, and both of them are bad.

  1. Your entire program would have to halt and wait for the Promise to execute before it could know what "foo" & "bar" would... nay, might be. (this is what "await," inside an async function, does in fact do: it pauses the entire function execution until the value is available or an the error is thrown)
  2. foo and bar would just be undefined (this is what actually happens), since, as executed synchronously, they'd just be non-existent properties of the top-level Promise object (which is not itself a "value," but rather a quasi-Monadic wrapper around getting an eventual value OR an error) which most likely doesn't even contain a value yet.
Share:
86,082
Tobias Mühl
Author by

Tobias Mühl

Updated on April 18, 2021

Comments

  • Tobias Mühl
    Tobias Mühl about 3 years

    I have a promise that returns data and I want to save that in variables. Is this impossible in JavaScript because of the async nature and do I need to use onResolve as a callback?

    Can I somehow use this (e.g. wrap it with async/await):

    const { foo, bar } = Promise.then(result => result.data, errorHandler);
    // rest of script
    

    instead of this?

    Promise.then(result => {
       const { foo, bar } = result.data;
       // rest of script
     }, errorHandler);
    

    Note: Bluebird library is used instead of native implementation, and I can't change from Promise to asnyc/await or Generators.

  • a better oliver
    a better oliver almost 8 years
    "it's awesome" Since when is blocking awesome?
  • silkAdmin
    silkAdmin almost 8 years
    @zeroflagL async isn't more blocking than promises callback. The thread is free to cary on processing other stuff (i.e. new requests if in NodeJS) while the execution will resume once the promise resolves. It's just a different syntax.
  • a better oliver
    a better oliver almost 8 years
    The execution of the function stops. It doesn't with Promise.then. So if you await a result in an event handler in a browser, it's definitely not awesome.
  • silkAdmin
    silkAdmin almost 8 years
    The downstream code in the handler wont execute but the thread will be freed up non the less. So browser wont freeze. Now if a function uses await at it's top level but the downstream code does other operation that do not requires the data returned by the async function then it's a design error.
  • a better oliver
    a better oliver almost 8 years
    So you think the user won't have a "freezing" experience if a scroll event or a key event takes seconds to be handled? I agree that await might be useful in a node server application or in a web worker, but not in front-end code. async is just sugar.
  • silkAdmin
    silkAdmin almost 8 years
    What makes the browser freeze is if the thread is blocked by synchronous operation. Which does not happen with async/await anymore than it does with a callback. Like you said it's just sugar but behind the scene it works out just the same.
  • a better oliver
    a better oliver almost 8 years
    You miss the point: The browser cannot continue scrolling as long as the scroll event handler has not finished its work. With await this can take a long time, with Promise.then not.
  • loganfsmyth
    loganfsmyth almost 8 years
    @zeroflagL You are incorrect, async/await does not block, it will execute the async function up to the first await and then suspend execution of the async function and return control to the parent scope and return a promise. When the value passed to await is available, the execution of the function will resume. There's an important difference between blocking and suspending execution.
  • a better oliver
    a better oliver almost 8 years
    @loganfsmyth Shame on me. I just realized that I forgot one important point: You can only await within async functions. I had regular functions in mind, which would be synchronous and therefore would block.
  • loganfsmyth
    loganfsmyth almost 8 years
    Ahh, that'll do it :) The example in this answer was definitely lacking solid example usage, and people often forget that.
  • Dtipson
    Dtipson almost 8 years
    Right, you can't "await" globally, only inside the context of a function that can be paused and resumed (because this is all just sugar on top of generators). External to the function, you're still dealing with a Promise, and you can't get an eventual value "out" of a Promise in that context, you can only map/flatMap over it to do things with it.
  • Zeor137
    Zeor137 almost 4 years
    I am having the same problem. And I understand the asyncronous problem. What I am struggling to to get is how to make the sync functions wait for the async if I need an async function to use await and I didn't understand how to get a value out of a then to that a sync variable after waiting for the function.
  • Dtipson
    Dtipson almost 4 years
    You can't, really. Synchronous functions (and code in the global scope) don't/can't wait for async ones. Like in the original example, the key is: what's actually IN the "//rest of script" bit. What is the rest of the script going to be? If it's ever doing something with the variables foo and bar, then those will simply be undefined in the "rest" of the script because it's all going to execute synchronously, not wait for the Promise to return. And not only will they be undefined, but they will ALWAYS remain undefined, because that scope just runs and is completed in one single pass.
  • Dtipson
    Dtipson almost 4 years
    Ultimately, async/await changes the WAY code runs in a function: it makes the familiar step-by-step synchronous syntax work differently than it normally would. But that logic is ultimately just expressing the functionality of callbacks in a non-callback-based way of writing things. The win is that we get to use the same sort of syntax throughout, without as much nesting, or getting into higher level concepts like types. But it is confusing, for precisely this reason: it looks the same, but works according to a different logic.
  • Zeor137
    Zeor137 almost 4 years
    I figure out that when I was trying to find out to do do what I wanted. I think a hard thing for us noobies to get is how to make these two kinds of functions to work together. For example I thought of using a global variable or a return value to use a value I needed from that async function while instead I had to the setState() function inside it.
  • Dtipson
    Dtipson almost 4 years
    Ah yep: if this is React you can think of setState as a sort of non-explicit callback. Everything in the script will run initially all at once, but then nothing will change or happen until some side-effect (like user input or a async request returning) triggers some additional actions, which then all happen in the context of the functions being called. It's definitely confusing because you CAN, in an async function, change variables set in the initial global scope. But you can't "get back out" to the original scope: it's done. It's in the past.
  • heltonbiker
    heltonbiker over 3 years
    "it's a good practice to clearly encapsulate all that behavior into a specific function and then hook that function up to the Promise": this part helped me a lot to understand how to properly organize this type of code, thanks.