passing an async response "through" a thunk - redux-thunk

Is it possible to pass an async "response" through a thunk? Given the action creator below:
export function emitLoadPlot(json_req) {
return function (dispatch) {
axios.post(URI, {
json_req
})
// .then(() => dispatch(
// emitTxRxAlert("yellow", "Received plot from server")
// ))
.then((response) => dispatch({
type: EMIT_PLOT_DATA_REQ,
payload: response
}))
.then(() => dispatch(
emitTxRxAlert("green", "Loaded plot model")
))
.then(() => dispatch({
type: EMIT_LOAD_PLOT
}))
.then(() => dispatch({
type: EMIT_VIEW_STATUS
}))
};
}
If I uncomment the Alert dispatch the response never makes it to where it's needed. Makes sense. Some of these calls (and the manipulation on the client end) take a long time to complete and I'd like to interleave client UI alerts (say) between when the async request returns in the browser and when it loads into the redux store. On this slow laptop the network request takes a lot less time than processing the response.
Is there any way for me to pass the response (or the reference to the response) through the first thunk above?

Calling then() returns a promise of the result of your callback.
Your callback returns whatever dispatch() returns, which is not the value you want.
Instead, you need to return the original value:
.then(response => {
dispatch(...);
return response;
})

Related

Incorporating the deepai text generator into a node.js project

I want it to log the ai generated text after the api has returned something but it doesn't seem to be waiting for the variable and only logs it after waiting.
//don't use await if you are using then operator on promise
function lyrics(message) {
var resp = deepai.callStandardApi("text-generator", {
text: fs.createReadStream("./Images/Elma.txt"),
}).then(
console.log(resp)
).catch(err => console.log(err));
console.log(resp);
As it's an async call which its result might returned only after the async call, don't use the returned value; You should specify & use the then's callback param as follows:
function lyrics(message) {
deepai.callStandardApi("text-generator", {
text: fs.createReadStream("./Images/Elma.txt"),
}).then(resp)(
console.log(resp)
).catch(err => console.log(err));
}
BTW, notice that you don't use the declared message param.

Handling sequentials http calls with RxJS and get response as soon as it arrives

I'm trying to write a JS service that will potentially send a number of calls to an external API at the same time and, as the API is rate limited, I want to stagger the calls and handle the responses separately.
I could use concatMap or mergeMap (with the concurrency option) but I have no idea how to handle the responses like I mentioned.
If service A, service B and service C tell the HTTP service to send 3 separate requests, I need the function in service A to get the first answer in its own pipe.
http.js:
let subscribe;
const observable$ = Observable.create((sub) => {
subscribe = sub;
});
function generateRequest(ajaxParams){
subscribe.next(ajaxParams);
}
observable$
.pipe(
mergeMap((params) => {
return ajax(params);
}, response => response, 2)
)
.subscribe((response) => {
console.log(response);
});
observable$ being the stream where I send the http params and the pipe handles the ajax instantiation and the delays/concurrency.
So my question is, how would I do something like this:
serviceA.js:
generateRequest(ajaxParams).pipe(map(response => response.data), tap((data) => {
console.log('Here is the data', data);
})).subscribe(() => {})
You can achieve that by combining all of your Observables without subscribing to them first, as demonstrated below:
const service_A_Observable = generateRequest(ajaxParams)
.pipe(
map(response => response.data),
tap((data) => {
// handle stream here...
})
);
const service_B_Observable = generateRequest(ajaxParams)
.pipe(
map(response => response.data),
tap((data) => {
// handle stream here...
})
);
from([service_A_Observable, service_B_Observable]).pipe(concatMap(obs => obs)).subscribe();
Or optionally customizing the desired concurrency:
from([...]).pipe(mergeMap(o => o, MAX_PARALLEL)).subscribe();

Test for .catch(() block fails in Jest

I am trying to test that a Vuex action's .catch(() block is reached given a certain API response, and that it returns the error. The catch is reached, but the test fails since it is expecting the actual API response, instead of the error that I throw.
The action that I am testing is:
getPageItems ({ commit, state, }) {
const page = state.page;
return testApi.fetch(`${pageNumber}`).then((response) => {
try {
isValid(response);
commit('addItemsToList', response);
} catch (error) {
console.error(error);
}
},
export const isValid = (response) => {
response.name ? true : throw new Error('invalid item');
};
The test I have is:
test('errors caught', async () => {
const item = {};
const commit = jest.fn();
const state = {
pageNumber: 2,
};
testApi.fetch.mockRejectedValue(item);
expect.assertions(1);
await getPageItems({ commit, state, }).catch((e) => expect(e).toBe('invalid item');
});
This test fails, as it expects e to be item (the response), and not the error. I'm not sure why this is the case.
mockApi.get.mockResolvedValue(item) results in fulfilled promise, none of catch callbacks will be called.
catch makes getPageItems unconditionally resolve with fulfilled promise, another catch callback after getPageItems() will never be called. It doesn't cause bad response error either. getPageItems returns a fulfilled promise and conditionally calls console.error, this is what needs to be tested.
This test doesn't return a promise, even if a rejection was asserted, it would be ignored. async..await is the way to chain promises correctly:
test('errors are caught', async () => {
mockApi.get.mockResolvedValue();
jest.spyOn(console, 'error');
await getPageItems({ commit, state });
expect(console.error).toHaveBeenCalledWith('bad response'));
});

Proper way to run asynchronous function in a Promise

I am making a test app using systeminformation. I'm trying to make it so that each then waits for the previous function to finish. The problem I'm having is that the functions I am running inside are also promises, so the next then runs before the function finishes.
const si = require('systeminformation');
var cpuObj;
function initCPU() {
return new Promise(resolve => {
si.cpu()
.then(data => cpuObj = data)
.catch(err => console.log(err))
.then(() => {
setTimeout(() => console.log("timer"), 3000);
})
.then(() => {
si.cpuTemperature().then(data => console.log(data));
})
.then(() => {
console.log("here");
});
});
}
function test() {
console.log(cpuObj);
}
initCPU().then(() => {
test();
});
Output:
here
{ main: -1, cores: [], max: -1 }
timer
Expected Output:
{ main: -1, cores: [], max: -1 }
timer
here
A few points that need to be addressed:
setTimeout() does not return a promise, so you need to promisify and return it.
Flatten your chain by returning the promises from within each of the continuations rather than attempting to chain continuations within other continuations (i.e. then() inside of then()).
Do not wrap the continuation chain with a promise constructor, as the chain itself is already a promise, just return it directly instead. This is considered an antipattern.
Do not use globals, because it makes the initCPU() no longer re-entrant safe. Multiple calls to initCPU() before the promise returned by the first call resolves will result in unexpected behavior otherwise. Instead, use the appropriate scope to pass values along, which in this case is the function itself.
Allow errors to propagate to the caller and let the caller decide how to handle the error. Do not handle errors from within initCPU() unless you expect to use a fallback and continue to provide meaningful data to the caller.
const si = require('systeminformation');
const delay = ms => new Promise(resolve => { setTimeout(resolve, ms); });
function initCPU() {
// use local scope, not global
let cpuObj;
// return this promise chain directly
return si.cpu()
.then(data => {
cpuObj = data;
// return the promise to the chain
return delay(3000);
})
// let caller handle errors
// .catch(err => console.log(err))
// flatten your chain
.then(() => {
console.log('timer');
// return the promise to the chain
return si.cpuTemperature();
})
// flatten your chain
.then(data => {
console.log(data);
console.log('here');
// pass data to caller
return cpuObj;
});
}
function test(cpuObj) {
// received from last continuation of initCPU()
console.log(cpuObj);
}
initCPU()
.then(test)
// handle error from caller
.catch(err => {
console.log(err);
});
If you just want to query the cpu object immediately, and query cpuTemperature after 3 seconds, I'd do something like this using Promise.all():
// default to 3 seconds, allow it to be configurable
function initCPU(ms = 3000) {
return Promise.all([
si.cpu(),
delay(ms).then(() => si.cpuTemperature())
]).then(([cpu, cpuTemperature]) => ({
cpu,
cpuTemperature
}));
}
function test (obj) {
console.log(obj.cpu);
console.log(obj.cpuTemperature);
}
initCPU()
.then(test)
.catch(err => {
console.log(err);
});

Nest JS - Issue writing Jest Test Case for a function returning Observable Axios Response

I am fairly new to NestJS + Typescript + RxJs tech stack. I am trying to write a unit test case using Jest for one of my functions but not sure if doing it correctly.
component.service.ts
public fetchComponents(queryParams) {
const url = this.prepareUrl(queryParams);
const data$ = this.httpService.get(url);
return data$
.pipe(map(({ data }) => data));
}
component.sevice.spec.ts
Test case works and passes
describe('fetchComponents', () => {
const query = {
limit: 10,
offset: 0
};
const result: AxiosResponse = {
data: 'Components',
status: 200,
statusText: 'OK',
headers: {},
config: {}
};
it('should return Dummy Data when called successfully', () => {
componentService.prepareUrl = jest.fn();
jest.spyOn(httpService, 'get').mockImplementation(() => of(result));
componentService.fetchComponents(market, query)
.subscribe(
(res) => {
expect(res).toEqual('Components');
}
);
});
});
Can you please provide suggestions and pointers on how exactly I should test this function. Also without using Library like marbel-rx
I am not sure if I am testing it correctly. Is there something else also which I should test?
Since Observables are asynchronous, you have to add the asynchronous done paramter and call done() after the expect that is executed last. Otherwise, jest will finish the test run after subscribe() is called without waiting for the execution of the asynchronous execution of subscribe's callback. Try to make your test fail by for example by expecting 'Komponents'. The test will not fail.
Also, I'd recommend to use mockImplementationOnce instead of mockImplementation when possible, to avoid implicitly reusing mock behaviors in later calls and therewith creating implicit dependencies.
it('should return Dummy Data when called successfully', done => {
// Add done parameter ^^^^
componentService.prepareUrl = jest.fn();
jest.spyOn(httpService, 'get').mockImplementationOnce(() => of(result));
// Prefer mockImplementationOnce ^^^^
componentService.fetchComponents(market, query)
.subscribe(
(res) => {
expect(res).toEqual('Components');
done();
// ^^^^^^ Call done() when test is finished
}
);
});

Resources