How to Return From Observable in TypeScript Method with Return Type - return-type

I have a Google geocode service in TypeScript. I need this method to return a "GoogleMap" class, after it fetches the lat/long for a given address.
I created a TypeScript method that returns a "GoogleMap" type. But, I'm getting a
function that is neither void nor any must return a value...
Here's my method:
getLatLongFromAddress(streetAddress: string): GoogleMap {
this.geoCodeURL = GOOGLE_GEOCODE_BASE_URL + streetAddress +
"&key=" + GOOGLE_MAPS_API_KEY;
this.googleMap = new GoogleMap();
this.httpService
.get(this.geoCodeURL)
.subscribe((data) => {
this.googleMap.lat = data.results.geometry.location.lat;
this.googleMap.long = data.results.geometry.location.lng;
return this.googleMap;
},
(error) => {
console.error(this.geoCodeURL + ". " + error);
return Observable.throw("System encountered an error: " + error);
},
() => {
console.info("ok: " + this.geoCodeURL);
return this.googleMap;
});
}
I can understand the http call will be async and the flow ought to continue to the bottom of the method, possibly before the response returns data. To return a "GoogleMap", do I need to await this Observable? How do I go about doing this?
Thanks!
UPDATE: 4/21/16
I finally stumbled on an approach that I'm finding some satisfaction. I know there's a number of posts from developers begging for a "real" service. They want to pass a value to the service and get an object back. Instead, many of the answers don't fully solve the problem. The simplistic answer usually includes a subscribe() on the caller's side. The down-side to this pattern, not usually mentioned, is that you're having to map the raw data retrieved in the service in the caller's callback. It might be ok, if you only called this service from this one location in your code. But, ordinarily, you'll be calling the service from different places in your code. So, everytime, you'll map that object again and again in your caller's callback. What if you add a field to the object? Now, you have to hunt for all the callers in your code to update the mapping.
So, here's my approach. We can't get away from subscribe(), and we don't want to. In that vein, our service will return an Observable<T> with an observer that has our precious cargo. From the caller, we'll initialize a variable, Observable<T>, and it will get the service's Observable<T>. Next, we'll subscribe to this object. Finally, you get your "T"! from your service.
Take my example, now modified. Take note of the changes. First, our geocoding service:
getLatLongFromAddress(streetAddress: string): Observable<GoogleMap> {
...
return Observable.create(observer => {
this.httpService
.get(this.geoCodeURL)
.subscribe((data) => {
...
observer.next(this.googleMap);
observer.complete();
}
So, we're wrapping the googleMap object inside the "observer". Let's look at the caller, now:
Add this property:
private _gMapObservable: Observable<GoogleMap>;
Caller:
getLatLongs(streetAddress: string) {
this._gMapObservable = this.geoService.getLatLongFromAddress(this.streetAddr);
this._gMapObservable.subscribe((data)=>{
this.googleMap = data;
});
}
If you notice, there's no mapping in the caller! you just get your object back. All the complex mapping logic is done in the service in one place. So code maintainability is enhanced. Hope this helps.

Your getLatLongFromAddress's signature says it will return a GoogleMap, however, nothing is ever returned (ie the return value of your function, as it stands, will be undefined).
You can get rid of this compilation error by updating your method signature:
// Return type is actually void, because nothing is returned by this function.
getLatLongFromAddress(streetAddress: string): void {
this.geoCodeURL = GOOGLE_GEOCODE_BASE_URL + streetAddress +
"&key=" + GOOGLE_MAPS_API_KEY;
this.httpService
.get(this.geoCodeURL)
.subscribe((data) => {
this.googleMap = new GoogleMap();
this.googleMap.lat = data.results.geometry.location.lat;
this.googleMap.long = data.results.geometry.location.lng;
},
(error) => {
console.error(this.geoCodeURL + ". " + error);
return Observable.throw("System encountered an error: " + error);
},
() => {
console.info("ok: " + this.geoCodeURL);
return this.googleMap;
});
}
Additional tidbit, I don't think the onError and onComplete callback return values are used by Rx (looking at the documentation, the signature for these callbacks has a return value of void), although I could be mistaken.

Related

Cypress returning Synchronous value within Async command?

So I think this is probably me mixing up sync/async code (Mainly because Cypress has told me so) but I have a function within a page object within Cypress that is searching for customer data. I need to use this data later on in my test case to confirm the values.
Here is my function:
searchCustomer(searchText: string) {
this.customerInput.type(searchText)
this.searchButton.click()
cy.wait('#{AliasedCustomerRequest}').then(intercept => {
const data = intercept.response.body.data
console.log('Response Data: \n')
console.log(data)
if (data.length > 0) {
{Click some drop downdowns }
return data < ----I think here is the problem
} else {
{Do other stuff }
}
})
}
and in my test case itself:
let customerData = searchAndSelectCustomerIfExist('Joe Schmoe')
//Do some stuff with customerData (Probably fill in some form fields and confirm values)
So You can see what I am trying to do, if we search and find a customer I need to store that data for my test case (so I can then run some cy.validate commands and check if the values exist/etc....)
Cypress basically told me I was wrong via the error message:
cy.then() failed because you are mixing up async and sync code.
In your callback function you invoked 1 or more cy commands but then
returned a synchronous value.
Cypress commands are asynchronous and it doesn't make sense to queue
cy commands and yet return a synchronous value.
You likely forgot to properly chain the cy commands using another
cy.then().
So obviously I am mixing up async/sync code. But since the return was within the .then() I was thinking this would work. But I assume in my test case that doesn't work since the commands run synchronously I assume?
Since you have Cypress commands inside the function, you need to return the chain and use .then() on the returned value.
Also you need to return something from the else branch that's not going to break the code that uses the method, e.g an empty array.
searchCustomer(searchText: string): Chainable<any[]> {
this.customerInput.type(searchText)
this.searchButton.click()
return cy.wait('#{AliasedCustomerRequest}').then(intercept => {
const data = intercept.response.body.data
console.log('Response Data: \n')
console.log(data)
if (data.length) {
{Click some drop downdowns }
return data
} else {
{Do other stuff }
return []
}
})
}
// using
searchCustomer('my-customer').then((data: any[]) => {
if (data.length) {
}
})
Finally "Click some drop downdowns" is asynchronous code, and you may get headaches calling that inside the search.
It would be better to do those actions after the result is passed back. That also makes your code cleaner (easier to understand) since searchCustomer() does only that, has no side effects.
you just need to add return before the cy.wait
here's a bare-bones example
it("test", () => {
function searchCustomer() {
return cy.wait(100).then(intercept => {
const data = {text: "my data"}
return data
})
}
const myCustomer = searchCustomer()
myCustomer.should("have.key", "text")
myCustomer.its("text").should("eq", "my data")
});

Make Http Request Observation and return anm Observable with the result

I have the following scenario: There is a service which is called "ContextProvider" that holds information regarding the context of the applicaiton (Logged In User, Things he can acess, etc). Right now I am observing this as following:
this.contextProvider.Context.subscribe(context => {
//Do Something
})
Now I have a service that will also be observable. I want this service to observe the context and return an observable. This would be easy with the map function:
let observable = this.contextProvider.Context.pipe(map(context => {
let aux: number = somevar + someothervar;
return aux;
})) //observable variable now holds the type Observable<number>
My scenario is a little bit more complex, because in order to fetch the result, I have to make an Http call, which is also an observable/promise:
let observable = this.contextProvider.Context.pipe(map(context => {
return this.httpClient.get<number>("Some URL").pipe(take(1));
})); //observable var now holds Obsevable<Observable<number>>
How can I make the "observable" var hold Observable?
EDIT: The URL value depends on the some values of the "context" variable
If I understand right your problem, you need to use concatMap for this case, like this
this.contextProvider.Context.pipe(
concatMap(context => {
return this.httpClient.get<number>("Some URL" + context.someData);
}));
You can find more patterns around the use of Observables with http calls in this article

Does promise object always need callback function?

I have a requirement that I need to create a service in Angular 1 and load data with an API call which can be accessed in controllers. I earlier tried by making a API call with $http service and assigning the data to the variable / object. I then injected the service and assigned the variable / object to the controller scope variable / object.
What I observed in the controller event loop is not same as service event loop and controller scope variable / object remains undefined. Later I got a solution to work by returning a promise from the service, and calling the promise in the controller, however I'm new to promises and not able to fully absorb that when I called a promise, I had to pass the function as argument which I think is a callback for the $http API call, but I'm uncertain how it's working under the hood. Can someone explain it?
//service code
this.getuserimages = function(uname) {
console.log("Username in UserImage Service: " + uname);
var promise = $http.get('/api/images/' + uname).then(function(response) {
this.userimages = response.data;
return this.userimages;
});
return promise;
}
// controller code
var userimagespromise = userImageService.getuserimages($routeParams.username);
userimagespromise.then(function(data) {
$scope.userimages = data;
Your code is a Promise chain.
I rewrited your code in a way that this chain is more clear, but the meaning is exactly the same:
$http.get('/api/images/' + uname)
.then(function(response) {
this.userimages = response.data;
return this.userimages;
})
.then(function(images) {
$scope.userimages = images;
});
You can read this flow in this way:
Please get me user images
And then, we they will be available (=> returned from the get and passed to the then function), save them in a variable and return them to the next element of the chain
And then, we they will be available (=> return from the previous promise), set them in the $scope
Please note that the entire flow is asynchronous because every Promise is "an object representing the eventual completion or failure of an asynchronous operation".
You can find more information in the Promise documentation.

Outputting Value of Promise during multiple parses

I am loading a promise from d3.csv and then making multiple changes to the returned array in subsequent .then calls.
I need to out put to the html (Angular 2) the sate of the array before each change, I can do this using the ( variable | async ) but it will update with each change and I need to output the state before each change.
I tried to clone the promise, but all clones just point to the same promise. Any variables are out of scope in the changes, and the parent scope is not reachable.
data = d3.csv promise
data.then(methodB()). // HTML | async Output Required of Array before changes
then(methodB()) // HTML | async Output Required of Array before changes
etc..
etc..
etc..
(There are around 15 methods applied to the data as it is munched and analyzed)
What is the best way to achieve this?
Assuming :
you have a starting promise named csvPromise, which delivers array
the methods to be applied are methodA, methodB, methodC etc, each of which accepts Array
each method either returns a mutation of the input array or delivers the mutation via a Promise
changes to the array are cumulative, method-on-method
you have a synchronous function output() which will accept the original array and each mutation
then a pattern like this will do the job :
csvPromise
.then(function(arr) {
output(arr); // observe initial array as delivered by csvPromise
return methodA(arr);
})
.then(function(arr) {
output(arr); // observe array after application of methodA
return methodB(arr);
})
.then(function(arr) {
output(arr); // observe array after application of methodB
return methodC(arr);
})
etc..
etc..
etc..
.then(function(arr) {
output(arr); // observe array after application of final method
}).catch(function(error) {
console.log(error); // any error thrown from anywhere in the chain will arrive here
});
The pattern can be proceduralised by building the chain dynamically as follows :
var methods = [methodA, methodB, methodC, ...]; // list of methods (uncalled at this point)
return methods.reduce(function(promise, method) {
return promise.then(function(arr) {
output(arr); // observe result of previous stage
return method(arr);
});
}, csvPromise)
.then(function(arr) {
output(arr); // observe array after application of final method
}).catch(function(error) {
console.log(error); // any error thrown from anywhere in the chain will arrive here
});
The most likely violation of the assumptions would be that output() was itself asynchronous, in which case :
var methods = [methodA, methodB, methodC, ...]; // list of methods
return methods.reduce(function(promise, method) {
return promise.then(function(arr) {
return output(arr).then(function() {
return method(arr);
});
});
}, csvPromise)
.then(function(arr) {
output(arr); // observe array after application of final method
}).catch(function(error) {
console.log(error); // any error thrown from anywhere in the chain will arrive here
});

Need correct call to Promise reduce (when.reduce )

I have a processor function that takes a "cmd" object and returns a promise where the resolution is the same "cmd" object passed in (with a response key added). reduce here is when.reduce
reduce = require('when').reduce;
//return processor(cmds[0])
return reduce(cmds, function(processor, cmd) {
Debug.L1('running processor for component ', cmd.component)
return processor(cmd)
})
.then(cmds => {
Debug.L1('cmds with responses\n', cmds)
let response = cmds.map(cmd => {
return cmd.response
})
console.log('the complete response is\n', response)
});
This does nothing, it does get to the .then but the array of promises never fires, never see the Debug running processor...
If I run just a single processor it works great cmd[0], cmds[1], etc.
return processor(cmds[0])
//return reduce(cmds, function(processor,cmd) {
// Debug.L1('running processor for component ', cmd.component)
// return processor(cmd) })
What am I missing here? Their api and wiki examples aren't giving me any insight.
IMPORTANT UPDATE:
The answer below does work but throws unhandled rejection errors. The culprit is the when library. It seems no longer active and has not been updated since node 6. I switched to bluebird and it works fine without any change to the code outlined below.
I'm still not sure what you are looking for, but it might be
reduce(cmds, function(responses, cmd) {
return processor(cmd).then(function(response) {
responses.push(response); // or whatever
return responses;
});
}, []).then(function(responses) {
…
});
Before trying to understand when.reduce, you might want to have a look at the non-promise array reduce.

Resources