Subscribe - Angular 2 - javascript

I have a file where I code my whole connection with the REST service, and it works.
From another file, I am executing the following lines (everything works)
this.baseService.getCars(ID)
.subscribe(cars=> this.cars= cars);
To access to the values of the response I was using HTML. For example: *ngIf="cars"
Now, I would like to access by Javascript to the variable doing this:
this.baseService.getCars(ID)
.subscribe(cars=> this.cars= cars);
console.log(this.cars)
but I get undefined but I can access by HTML. I know that it is a stu**d question, but how should I do it? Which variable does contain the variable?

The execution order of those lines of code is not what you think it is.
To see cars in console, change your function to this:
this.baseService.getCars(ID)
.subscribe(cars=>{
this.cars= cars;
console.log(this.cars);
});

You need to place the console.log inside subscribe
this.baseService.getCars(ID)
.subscribe(
cars=> {
this.cars= cars;
console.log(this.cars);
},
error => {
console.log(error);
}
);

Subscribe is asynchronous, like a Promise, but isn't a Promise so, when you execute the code, the subscribe is fired, then the console log. But When the console.log is executing, subscribe is running yet, so that's why you get undefined.
You can do the console.log inside the callback function in subscribe
this.baseService
.getCars(ID)
.subscribe(cars=> {
this.cars = cars
console.log(this.cars)
});
Another solution is to use async/await. You can't use async/await directly with subscribe, because IT'S NOT A PROMISE. Fortunately Observers can be converted to a Promise.
So, in you service you can return a promise, like this:
getCars() {
// your service stuff
return this.api.get(url).toPromise().then( res => res.data); // This is the important part.
}
Then, in your component, call it with async/await:
async yourFunction() {
this.cars = await this.baseService.getCars(ID);
console.log(this.cars);
}
Now you can log this.cars after the getCars()
Hope this helps you.

Related

Access a variable outside of .then function [duplicate]

This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 3 years ago.
I have some code that authenticates by posting an object using npm request.
After posting the JSON object, a JSON response is returned which contains an authn token I can use in future GET/POST request headers.
I have some async code that returns the correct authn token but I can only access it via the .then function code block.
I have read through the usual linked thread here: How do I return the response from an asynchronous call? but even though the return result is in the .then function I still get undefined when trying to do anything other than console.log().
const postData = {
"auth": {
"username": "username",
"password":"password"
}
};
var returnRequest = () => {
var options = {
method: 'POST',
url: 'https://api.appnexus.com/auth',
body: postData,
json: true
};
return new Promise(async (resolve, reject) => {
await requestAPI(options, function (error, response, body) {
if (error) {
reject(error);
}
resolve(body);
});
})
}
var returnedResult
returnRequest()
.then((result) => {
returnedResult = result.response.token
})
.catch((error) => {
console.log(error);
})
console.log(returnedResult)
I would expect to see the returnedResult store the token as I understand it, the .then promise only runs one the request has happened?
A developer said I have to build all subsequent code inside the .then block but that sounds crazy, that I have to have my whole program inside this returnRequest function rather than be able to pass the returned token back outside to a global variable?
Is that the correct way to do it, and am I supposed to just build all subsequent requests using the result.response.token inside the
returnRequest()
.then((result) => {
returnedResult = result.response.token
})
function?
.then is the mechanism that promises use to let you know when the value is available. The "when" part is important: only the promise object knows what time your code should run at. So even if you try to write some extra code to store values in variables, the question of when it's safe to try to get those variables can only be answered by the promise's .then method.
So yes, any code that needs the values to be available needs to be put in the .then of the promise. Maybe you have some separate part of the codebase that needs to interact with the result, and so it feels clumsy to try to have to copy that code over to here. Well you don't need to: you just need to pass that other code the promise, and then that other code can call .then on the promise itself. For example:
const tokenPromise = returnRequest()
.then(result => result.response.token);
// Anywhere else that tokenPromise in scope can write whatever code it needs to:
tokenPromise.then(token => {
// Do anything with the token
});
// And a completely different piece of code can write its own stuff with the token
tokenPromise.then(token => {
// Do other stuff with the token
});
No you don't need to use result.response.token everywhere to use the authn token.
The thing here to understand is the flow of code. Your console.log statement may be returning you undefined .
Why ? Haven't you updated the global variable inside the then block of promise ?
Yes you have ! But the problem is that it is not reflected to you in the console log statement because this very statement is executed before any updation in the global variable.
So, it gets updated but it takes time to do so.
This is what is known as asynchronous code .
Now what about the suggestion of wrapping the code inside the .then block.
If you will add a console log statement beneath the updation (inside the then block) it will print you the exact token you are looking for.
But actually you don't need that , you can use aysnc/ await syntax to make it look like synchronus code, and then it will don't confuse you.
For example you can do something like this.
let result = await returnRequest();
let returnedToken =result.response.token;
// Now it will print you the token
console.log(returnedToken)
Make sure to add the async keyword infront of the function using await.
there are several ways to do what you ask, one way would be to wrap your entire code in async iife (immediately invoked function expression) so that you can use await.
!async function(){
....
....
....
var returnedResult = await returnRequest()
.then((result) => {
return result.response.token;
})
.catch((error) => {
console.log(error);
})
//continue
}()
I’ll try and answer parts of this question.
The setting of value for global variable inside of the .then callback is correct and you’ll have the value inside the “then” block. You can console.log inside of it and check.
The console.log outside in the “global” scope runs even before the the promise is resolved. Remember that java script is even driven. It registers the api call and continues executing the next line of it can. Which is why you’ll see an undefined value of token.
If all your subsequent requests depend on the auth token and you need to call some other API in the same call, you’ll have to do it in the .then call or create a promise chain with multiple .then which is essentially the main benefit of Promises. Previous result is passed on to the next promise.

How do I get result from function with a get request?

I have a piece of code that submits a GET request to another part of my website in a function.
function getStatus(ID) {
$.get('/api/'+ID+'/info', function(statusCallback) {
return statusCallback;
});
}
console.log(getStatus(ID));
What I would expect this code to return and then log would be the information that I need.
What I actually get in console log is
undefined
What can I do to get the actual result?
You're doing async operation. In your case using callback. If you want to print statusCallback, you have to console.log it like Christos mentioned.
Also
console.log(getStatusId())
will return undefined because it's default value returned from the function that has no explicit return. Your function has no return statement, it's only calling some async method.
Btw, try promises instead of callbacks if you can ;)
With ES7 you can use async await
async function getStatus(ID) {
$.get('/api/'+ID+'/info', function(statusCallback) {
// This is executed in the future, but await knows how to deal with it
return statusCallback;
});
}
console.log(await getStatus(ID));
They are a good practice to get started with, because the code gets a lot easier to read.
You need to change your functions as below:
function getStatus(ID) {
$.get('/api/'+ID+'/info', function(statusCallback) {
console.log(statusCallback);
});
}
The function you pass as the second parameter of your get call is called success callback. It will be called, once the get request that is issued on each call of the getStatus function completes successfully. Only then you have access to what the server returned and you can fairly easy access it as above.
Update
If you want to return the data that server sends you have to declare first a variable
function getDataById(ID){
function callback(data){
return data;
}
$.get('/api/'+ID+'/info', function(data) {
return callback(data);
});
}

How to access values outside of axios request?

Very easy question. When I run console.log on the jsonPayload in line 6, I see the expected output. When I run console.log again on jsonPayload in the last line, it returns an empty {}. How do I access the payload outside of the initial request?
var jsonPayload = {}
axios.get('http://localhost:8080/api/tools')
.then(function (response) {
jsonPayload = response.data[0]
// this returns the expected payload
console.log(jsonPayload)
})
.catch(function (error) {
console.log(error)
})
// this returns empty {}
console.log(jsonPayload)
I am not sure why you would like to access the jsonPayload outside of the axios get call, but I do know why you are receiving a log of {} from the outside console.log().
axios.get()
The above method will return a promise. This promise allows you to protect your next process from receiving empty data.
Inside the .then() method, you are able to retrieve the data that is sent back to you from the axios.get() call. However, this data cannot reach you until the process of retrieving data from the API, is complete.
This is where you are running into your issue. The console.log() outside of the .then() method is triggering before the completion of the ajax call.
You could also look into using ES7 async and await. This allows you to write these axios calls without the .then().
You cannot do this because the request is processed asynchronously.
As #d3L answered, you're limited to handle the response within the callback passed to .then. However, after ES8 you can use async / await which is an alternative to using typical Promise handling with callbacks. Is still asynchronous, but looks synchronous:
(async function() {
try {
const response = await axios.get('http://localhost:8080/api/tools');
console.log(response.data[0])
} catch(err) {
console.log(err);
}
})();
Note whether you're running it on node or the browser, the engine might not support it, so you might need to transpile the code using babel.

Angular2 Can't get data from response using promise?

I'm trying to get data out of response but I can't seem to get the result I want.
facebookLogin(): void {
this.fb.login()
.then((res: LoginResponse) => {
this.accessToken = res.authResponse.accessToken;
this.expiresIn = res.authResponse.expiresIn;
this.signedRequest = res.authResponse.signedRequest;
this.userId = res.authResponse.userID;
console.log('Logged In', res, this.accessToken); //works without problem
this.router.navigate(['../other-register']);
})
.catch(this.handleError);
console.log(this.accessToken) //printing 'undefined'
}
Here within then => { }, console.log seems to print the data in res without any problem. I can see data I want but when I console.log outside of then =>{ }, it's giving me undefined.
what am I doing wrong? I need to use data inside response and pass them to other component but I can't seem to figure out what I'm doing wrong.
Can anyone help me? Thanks
This is the expected behavior actually.
this.fb.login() is a Promise. This means that the value of the result/response (res) will not readily be available right when it is called, but it 'promises' that it will have a value once some action is taken or a response is returned and 'then' it will do something. In this case that action would be connecting to the Facebook API and having data returned. This is just like Ajax in jQuery if you have experience with that, Promises are a more evolved version of callbacks.
What is happening is that you function is being executed in this order:
this.fb.login() is called. Doesn't have a value yet so it allows the script to continue.
console.log() is called.
this.fb.login's value is returned and the then() closure is executed.
If you want to know when the value is return or perform a specific action once it is returned you can call a function within .then() or look into observables (RxJS) to notify other parts of your application that login was successful (or wasn't).
Observables Example
Here is one example on Observables, however, I would do more research as there are multiple Subjects to select from, all which have slightly different behavior. Also, this kind of pattern works better in Angular2+ if this is performed in a service, that way other components will be able to access the information provided by Facebook.
import { AsyncSubject } from 'rxjs/AsyncSubject';
// ...
response: AsyncSubject<any> = new AsyncSubject();
facebookLogin(): void {
this.fb.login()
.then((res: LoginResponse) => {
this.response.next(res);
this.response.complete();
this.router.navigate(['../other-register']);
})
.catch(this.handleError);
}
You then retrieve the data from within response like this:
this.response.subscribe(result => {
console.log(result);
})
Pass Data Example
Since you already have a function in the service designed to receive the data, this may be a wiser implementation in your case.
facebookLogin(): void {
this.fb.login()
.then((res: LoginResponse) => {
this.user_service.passData(res.authResponse.accessToken);
this.router.navigate(['../other-register']);
})
.catch(this.handleError);
}

Observable determine if subscriber function has finished

What is the best way to determine if the subscriber has finished executing or better yet return something and catch it up-stream? For example:
this._subscriptions.push(this._client
.getCommandStream(this._command) // Returns an IObservable from a Subject stream
.subscribe(msg => {
// Do some processing maybe some promise stuff
http.request(url).then(
// some more stuff
);
});
What's the best know to determine that subscription has finished. I've implemented it as follows:
this._subscriptions.push(this._client
.getCommandStream(this._command)
.subscribe(msg => {
// Do some processing maybe some promise stuff
http.request(url).then(re => {
// some more stuff
msg.done()
}).catch(err => msg.done(err));
});
i.e. added a done method to the object being passed in to determine if this is finished. The issue with that is I'll have to call done in every promise or catch block and find that a little too exhaustive. Is there a cleaner and more automated way of doing this?
I think the examples I've given are not good enough. This implementation is using RX to build an internal messaging bus. The get command stream is actually returning a read-only channel (as an Observable) to get commands and process them. Now the processing could be a http request followed by many other things or just an if statement.
this._client
.getCommandStream(this._command) // Returns an IObservable from a Subject stream
.subscribe(msg => {
// Do some processing maybe some promise stuff
http.request(url).then({
// some more stuff
}).then({
// Here I wanna do some file io
if(x) {
file.read('path', (content) => {
msg.reply(content);
msg.done();
});
} else {
// Or maybe not do a file io or maybe even do some image processing
msg.reply("pong");
msg.done()
}
});
});
I feel like this is a fine usage of the Observable pattern as this is exactly a sequence of commands coming in and this logic would like to act on them. The question is notice msg.done() being called all over the place. I want to know what is the best way to limit that call and know when the entire thing is done. Another option is to wrap it all in a Promise but then again what's the difference between resolve or msg.done()?
Actually, making another asynchronous request inside subscribe() isn't recommended because it just makes things more complicated and using Rx in this way doesn't help you make your code more understandable.
Since you need to make a request to a remote service that returns a PRomise you can merge it into the chain:
this._subscriptions.push(this._client
.getCommandStream(this._command)
.concatMap(msg => http.request(url))
.subscribe(...)
Also the 3rd parameter to subscribe is a callback that is called when the source Observable completes.
You can also add your own teardown logic when the chain is being disposed. This is called after the complete callback in subscribe(...) is called:
const subscription = this._subscriptions.push(this._client
...
.subscribe(...)
subscription.add(() => doWhatever())
Btw, this is equivalent to using the finally() operator.
As per RxJs subscribe method documentation, the last Argument is completed function
var source = Rx.Observable.range(0, 3)
var subscription = source.subscribe(
function (x) {
console.log('Next: %s', x);
},
function (err) {
console.log('Error: %s', err);
},
function () {
console.log('Completed');
});
please refer this documentation
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/subscribe.md

Categories

Resources