Need correct call to Promise reduce (when.reduce ) - promise

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.

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")
});

How do I refactor a traditional synchronous loop with RxJS?

I'm new to RxJS and trying to wrap my brain around how I should be writing my code. I'm trying to write a function that extends an existing http which returns an observable array of data. I'd like to then loop over the array and make an http request on each object and return the new array with the modified data.
Here's what I have so far:
private mapEligibilitiesToBulk(bulkWarranties: Observable<any[]>): Observable<IDevice[]> {
const warranties: IDevice[] = [];
bulkWarranties.subscribe((bulk: any[]) => {
for (let warranty of bulk) {
// Check if another device already has the information
const foundIndex = warranties.findIndex((extended: IDevice) => {
try {
return warranty.device.stockKeepingId.equals(extended.part.partNumber);
} catch (err) {
return false;
}
});
// Fetch the information if not
if (foundIndex > -1) {
warranty.eligibilityOptions = warranties[foundIndex];
} else {
this.getDevices(warranty.device.deviceId.serialNumber).subscribe((devices: IDevice[]) => {
warranty = devices[0];
}); // http request that returns an observable of IDevice
}
warranties.push(warranty);
}
});
return observableOf(warranties);
}
Currently, my code returns an observable array immediately, however, its empty and doesn't react the way I'd like. Any advice or recommended reading would be greatly appreciated!
Without knowing a lot more about your data and what would make sense, it is impossible to give you the exact code you would need. However, I made some assumptions and put together this StackBlitz to show one possible way to approach this. The big assumption here is that the data is groupable and what you are actually trying to achieve is making only a single http call for each unique warranty.device.stockKeepingId.
I offer this code as a starting point for you, in the hopes it gets you a little closer to what you are trying to achieve. From the StackBlitz, here is the relevant method:
public mapEligibilitiesToBulk(bulk: Warranty[]): Observable<IDevice[]> {
return from(bulk).pipe(
tap(warranty => console.log('in tap - warranty is ', warranty)),
groupBy(warranty => warranty.device.stockKeepingId),
mergeMap(group$ => group$.pipe(reduce((acc, cur) => [...acc, cur], []))),
tap(group => console.log('in tap - group is ', group)),
concatMap(group => this.getDevices(group[0].device.deviceId.serialNumber)),
tap(device => console.log('in tap - got this device back from api: ', device)),
toArray()
)
}
A couple of things to note:
Be sure to open up the console to see the results.
I changed the first parameter to an array rather than an observable, assuming you need a complete array to start with. Let me know if you want this to extend an existing observable, that is quite simple to achieve.
I put in some tap()s so you can see what the code does at two of the important points.
In the StackBlitz currently the getDevices() returns the same thing for every call, I did this for simplicity in mocking, not because I believe it would function that way. :)

Vuex store action from Promise to async/await

Currently I use promises in the store actions but want to convert it into async/await. This is an example of the store action with promises:
fetchActiveWorkspace (context, workspaceID) {
if (workspaceID) {
return this.$axios.get(`#api-v01/workspaces/workspace/${workspaceID}`)
.then(response => {
context.commit('setActiveWorkspace', response.data)
})
.catch(err => {
throw err
})
} else {
return Promise.resolve(true)
}
},
This fetchActiveWorkspace action is resolved in components because it returns promise. How can I convert this code snippet into a async/await structure and use it in components?
This is how I would try to translate it; take into account that as I have no access to the original code in full context, I cannot try it first-hand to make sure it works; but still, this is how you can use async/await with promises.
// 1. Mark the function as `async` (otherwise you cannot use `await` inside of it)
async fetchActiveWorkspace(context, workspaceID) {
if (workspaceID) {
// 2. Call the promise-returning function with `await` to wait for result before moving on.
// Capture the response in a varible (it used to go as argument for `then`)
let response = await this.$axios.get(`#api-v01/workspaces/workspace/${workspaceID}`);
context.commit('setActiveWorkspace', response.data);
}
// 3. I don't think this is necessary, as actions are not meant to return values and instead should be for asynchronous mutations.
else {
return true;
}
}
You can surround the function's body with try/catch in case you want to capture and handle exceptions. I didn't add it in order to keep things simple and because your promise-based code will just capture and re-throw the exception, without doing anything else.

How to get promise value correctly?

Consider the following code:
function toolsQueryResult(){
const query = `query{......}`
return request('http://...', query,).then(data => { return data })
}
var toolsQueryResult= toolsQueryResult();
var toolsNames = [];
toolsQueryResult.then(function(result){
result['key'].forEach(function(item){
toolsNames.push(item["name"])
})
})
console.log(toolsNames)
This returns and prints out empty list "[ ]" to me.Does any one know why?
But if I put "console.log()" between two final "})", it returns list of tools correctly.How should I work with this promise object to have list of tools correctly after second "})" at the end of code?
The reason is, your console.log statement is executed before the the promise toolsQueryResult is resolved. It would be really useful and helpful if you have debugger tools on and place breakpoints to see what i just said.
That said,having the console.log outside of the promise being resolved or rejected beats the whole purpose, meaning you are trying to output a statement before it could complete its execution, hence when you place the console.log statement inside of the then function it outputs result.
Fiddle with your code (modified the result to be a simple array) for you to debug and see : https://jsfiddle.net/jayas_godblessall/vz8mcteh/
or execute it here to see :
function toolsQueryResult() {
const query = `query{......}`
return request('http://...', query, ).then(data => {
return data
})
}
// just to emulate your api call
function request(foo) {
return Promise.resolve(["item1", "item2"]);
}
var toolsQueryResult = toolsQueryResult();
var toolsNames = [];
toolsQueryResult.then(function(result) {
result.forEach(function(item) {
toolsNames.push(item)
})
console.log("am executed after you waited for promise to complete - in this case successfully, so you can see the tool sets")
console.log(toolsNames)
})
console.log("am executed before you could resolve promise")
console.log(toolsNames)

How to Return From Observable in TypeScript Method with 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.

Resources