A couple of points I can see base on the code provided:
- Observables are asynchronous
- You can subscribe after the conditional block
Asynchronous
The observable you get back from http.get
does not emit immediately. It waits for the http response first, then executes the code inside subscribe
. It also does not block execution. So while it's waiting for the response, it carries on and runs return results
. When the response finally comes, then the subscribe
block runs.
Here's an example. I've simulated Angular's http.get
here since it's not so simple to include Angular into the snippet:
// Angular's `http.get` returns an RxJS Observable
// which `emits` and `completes` when the http response comes through
// So this simulates the `get`
// using an Observable which emits and completes after 500 milliseconds
const http = { get: () => Rx.Observable.of('messages').delay(500) }
const results = []
const url = ''
function getMessages() {
http.get(url).subscribe(messages => {
// this block is run after 500ms
results.push({});
console.log('C: ', results) // [{}]
})
return results; // this code does not wait 500ms, it runs immediately
}
const returned = getMessages()
console.log('A: ', returned) // []
console.log('B: ', results) // []
// logs will show:
// A: []
// B: []
// C: [{}]
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
Subscribing after the conditional block
const forkJoin = Rx.Observable.forkJoin
// Let's pretend our endpoint just response to `get` with the url as its "messages"
const http = {
get(url) {
return Rx.Observable.of([url]).delay(500)
}
}
const url1 = 'one.com', url2 = 'two.com', url3 = 'three.com'
let results = []
let oneReq
function getMessages() {
let response;
if (oneReq) {
response = http.get(url1) // emits [one.com]
} else {
response = forkJoin([
http.get(url2), http.get(url3)
]) //emits [[two.com], [three.com]]
}
// we return the Observable
return response.do(handleMessages)
}
function handleMessages(messages) {
// because of the forkJoin,
// you need to check for the nested array
// [[two.com], [one.com]]
// that forkJoin makes before you concat
results = results.concat(messages)
}
oneReq = true
getMessages().subscribe(messages => {
console.log(results)
})
oneReq = false
getMessages().subscribe(messages => {
console.log(results)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
Note that the Observable
is returned from getMessages()
.
To re-iterate on the asynchronous aspect, returning results
from getMessages()
will not produce what you might expect because code inside subscribe()
executes later.
Angular components can detect the changes for you though, so you have two choices inside a component if you don't like having to subscribe to getMessages():
Option 1:
// change the `do` to subscribe
function getMessages(): void {
// if/else...
response.subscribe(handleMessage)
}
Note that you cannot return the messages
now. Returning response.subscribe(handleMessage)
will return the Subscription, not the Observable. Take a look at this about subscriptions
Option 2:
Use the async
pipe in your template:
<ul>
<li *ngFor="let result of results | async">{{result}}</li>
<ul>
Here's the documentation and an article about the async pipe