69

I have a pretty silly problem. Consider the following:

vm.feed = getFeed().then(function(data) {return data;});

getFeed() returns a $q deferred promise (I am on angular) that resolves successfully.

My goal is to set vm.feed equal to the data value returned by the successful callback. As it is right now, the code simply assigns vm.feed equal to the $promise object returned by getFeed().

I know I could simply do: vm.feed = data inside the resolved function but I want to understand why this code does not work as it is.

PD: the promise resolves correctly and even after it has been resolved vm.feed keeps being equal to the Promise, and not data. I copy the console.log of vm.feed after +10 seconds have elapsed:

Promise {$$state: Object} $$state: Objectstatus:1 value: Object

That value property inside the Promise object contains the actual solution of the promise that I want to assign to vm.feed (e.i. data).

thank you!

Gerard Clos
  • 1,080
  • 2
  • 12
  • 21
  • use vm.feed = getFeed().then(function(data) {return data.data ;}); – ngLover Jul 07 '15 at 09:04
  • The promise is returned for a reason: The result is not available immediately, but after some delay. Assigning it to global variables opens the door for all kinds of synchronizing problems (is the data already there?). Best solution is to trigger all functionality involving the returned data in the callback of the promise. – Sirko Jul 07 '15 at 09:05
  • 1
    if `getFeed` use `$http` consider using `$resource` instead, it return an empty object (not a promise) that will be filled when resource respond – fantarama Jul 07 '15 at 09:08
  • Hi, the promise resolves correctly, and even once it has been solved vm.feed keeps being equal to `Promise`. I copy the console log of vm.feed after +10 seconds have been elapsed in the question body. – Gerard Clos Jul 07 '15 at 09:11
  • Hi fantarama, thanks for the tip, I would still like to know why the code I provided does not work. – Gerard Clos Jul 07 '15 at 09:17
  • Hi upendra, it does not work :( the actual solution is to do this: `vm.feed = getFeed().then(function(data) {vm.feed = data;});` but I just want to know why the code I provided in the question does not work. It must be related to some kind of misconception I have with Promises – Gerard Clos Jul 07 '15 at 09:24

5 Answers5

33

You are going to get whatever then() returns. But since you are reading this, the following may help you:

Your statement does nothing more than ask the interpreter to assign the value returned from then() to the vm.feed variable. then() returns you a Promise (as you can see here: https://github.com/angular/angular.js/blob/ce77c25b067b7b74d90de23bfb4aac6a27abb9d1/src/ng/q.js#L288). You could picture this by seeing that the Promise (a simple object) is being pulled out of the function and getting assigned to vm.feed. This happens as soon as the interpreter executes that line.

Since your successful callback does not run when you call then() but only when your promise gets resolved (at a later time, asynchronously) it would be impossible for then() to return its value for the caller. This is the default way Javascript works. This was the exact reason Promises were introduced, so you could ask the interpreter to push the value to you, in the form of a callback.

Though on a future version that is being worked on for JavaScript (ES2016) a couple keywords will be introduced to work pretty much as you are expecting right now. The good news is you can start writing code like this today through transpilation from ES2016 to the current widely supported version (ES5).

A nice introduction to the topic is available at: https://www.youtube.com/watch?v=lil4YCCXRYc

To use it right now you can transpile your code through Babel: https://babeljs.io/docs/usage/experimental/ (by running with --stage 1).

You can also see some examples here: https://github.com/lukehoban/ecmascript-asyncawait.

Nathan
  • 4,017
  • 2
  • 25
  • 20
  • 1
    I copied vm.feed value once the promise has resolved in the question body; it still is equal to the Promise object and not `data`, shouldn't it have changed once the promise has resolved? – Gerard Clos Jul 07 '15 at 09:19
  • 1
    In Javascript, variables have their value set by user code alone. Once the interpreter ran that line of code, it won't change the variable until some other instruction you gave sets its value to something else. – Nathan Jul 08 '15 at 03:05
  • 1
    FYI what you are trying to achieve will be available in ES2016 with the `await` keyword. In case you are interested: https://www.youtube.com/watch?v=lil4YCCXRYc :) – Nathan Jul 08 '15 at 09:15
  • is it better then to use .ajax to get your data from php query? I don't see any example code anywhere that assigns data from php encode_json from promise to a json array in javascript. I can only do this in .ajax so far. – Brian Wiley Dec 27 '20 at 04:08
  • https://stackoverflow.com/questions/66045243/angular-how-to-set-the-value-inside-the-function – Murugan Feb 04 '21 at 12:12
33

This could be updated to ES6 with arrow functions and look like:

getFeed().then(data => (vm.feed = data));

If you wish to use the async function, you could also do like that:

async function myFunction(){
    vm.feed = await getFeed();
    // do whatever you need with vm.feed below
 }

Edit: added parenthesis to be eslint correct (as commented by Max Waterman)

Soldeplata Saketos
  • 3,212
  • 1
  • 25
  • 38
  • https://stackoverflow.com/questions/66045243/angular-how-to-set-the-value-inside-the-function – Murugan Feb 04 '21 at 12:12
  • 2
    what if I want to return and export value of vm.feed and pass it further as an argument in a completely different function in a different js file which accepts argument of type String ( assumption: vm.feed contains data of type String). Here returning vm.feed is again going to be a Promise, so I can not pass it to that function. Is there no way we can retrieve the data from a Promise and assign it to a variable to be used as a non Promise object further? – Ajay Singh Mar 10 '21 at 05:28
  • Ajay, after the “await”, it’s not a promise any more. – Soldeplata Saketos Mar 10 '21 at 05:52
  • 2
    Thanks Soldeplata for the response! You are right, but if I return a value from inside async function that is again a Promise. So I can not return the non promise data for a further usage in a different function. – Ajay Singh Mar 10 '21 at 06:27
  • Ajay, await for that promise you return, and it will not be a Promise any more – Soldeplata Saketos Mar 10 '21 at 08:12
  • Ajay, if you want to return the promise, you don’t need async await – Soldeplata Saketos Mar 10 '21 at 08:13
  • 1
    side-note on that example with es6 arrow functions: https://eslint.org/docs/rules/no-return-assign – Max Waterman Jul 08 '21 at 11:42
14

The then() method returns a Promise. It takes two arguments, both are callback functions for the success and failure cases of the Promise. the promise object itself doesn't give you the resolved data directly, the interface of this object only provides the data via callbacks supplied. So, you have to do this like this:

getFeed().then(function(data) { vm.feed = data;});

The then() function returns the promise with a resolved value of the previous then() callback, allowing you the pass the value to subsequent callbacks:

promiseB = promiseA.then(function(result) {
  return result + 1;
});

// promiseB will be resolved immediately after promiseA is resolved
// and its value will be the result of promiseA incremented by 1
Pavel P
  • 15,789
  • 11
  • 79
  • 128
BhavO
  • 2,406
  • 1
  • 11
  • 13
  • 2
    I agree but if the callback returns the resolved data itself shouldn't it be available outside the Promise object? – Gerard Clos Jul 07 '15 at 09:20
  • 2
    the return value of the success callback is passed into the success callback of subsequent success callbacks, allowing you to pass the mutated value along the call chain (chain of responsibilty) – BhavO Jul 07 '15 at 09:23
  • But in this example if you declared vm.feed let's say at the beginning and then you console.logged vm.feed it would run as undefined. – FabricioG Jan 28 '20 at 16:37
1

You could provide your function with the object and its attribute. Next, do what you need to do inside the function. Finally, assign the value returned in the promise to the right place in your object. Here's an example:

let myFunction = function (vm, feed) {
    getFeed().then( data => {
        vm[feed] = data
    })
} 

myFunction(vm, "feed")

You can also write a self-invoking function if you want.

molexi
  • 530
  • 5
  • 13
-8

This is one "trick" you can do since your out of an async function so can't use await keywork

Do what you want to do with vm.feed inside a setTimeout

vm.feed = getFeed().then(function(data) {return data;});

 setTimeout(() => {
    // do you stuf here
    // after the time you promise will be revolved or rejected
    // if you need some of the values in here immediately out of settimeout
    // might occur an error if promise wore not yet resolved or rejected
    console.log("vm.feed",vm.feed);
 }, 100);