redux asynchronous action with await/async - asynchronous

From the tutorial located here, I have a question regarding this section of the code:
export function fetchPosts(subreddit) {
// Thunk middleware knows how to handle functions.
// It passes the dispatch method as an argument to the function,
// thus making it able to dispatch actions itself.
return function (dispatch) {
// First dispatch: the app state is updated to inform
// that the API call is starting.
dispatch(requestPosts(subreddit))
// The function called by the thunk middleware can return a value,
// that is passed on as the return value of the dispatch method.
// In this case, we return a promise to wait for.
// This is not required by thunk middleware, but it is convenient for us.
return fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then(
response => response.json(),
// Do not use catch, because that will also catch
// any errors in the dispatch and resulting render,
// causing an loop of 'Unexpected batch number' errors.
// https://github.com/facebook/react/issues/6895
error => console.log('An error occured.', error)
)
.then(json =>
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
dispatch(receivePosts(subreddit, json))
)
}
}
Let's assume I wanted to use the async/await syntax instead of the "then" syntax, how would I get the error object if something fails?
e.g.
let response = await fetch(`https://www.reddit.com/r/${subreddit}.json`)
let json = await response.json();
I can surround these lines of code with a try/catch, but the author has a stern warning not to use catch here (refer to snippet above).
So is there a proper way to use the async/await pattern with this code?

In the link you provided the note to avoid using catch is regarding the promise .catch statement. This is because it would catch errors in both the then blocks. Instead of just errors caused via fetch or response.json() it would also catch errors caused via dispatch(receivePosts(subreddit, json))
You should be able to use async await as you describe in your post whilst avoiding catching errors caused by dispatch. e.g.
export function fetchPosts(subreddit) {
return async function (dispatch) {
dispatch(requestPosts(subreddit));
let response;
let json;
try {
response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
json = await response.json();
} catch(e) {
// handle fetch or json error here e.g.
dispatch(receivePostsError(subreddit, e.message));
}
if (json) {
dispatch(receivePosts(subreddit, json));
}
}
}

Related

Does firebase transaction retry run codes beyond the transaction?

If there are more codes/logic outside the transaction that should only be run when the transaction succeeds, will these codes be ran after the retry succeed? See construed example below based on my Express route handler.
app.post('/some/path/to/endpoint', async (req, res) => {
try {
await db.runTransaction(async t => {
const snapshot = t.get(someDocRef);
const data = snapshot.data();
doSomething(snapshot);
return t.update(snapshot.ref, { someChanges });
});
// QUESTION: If transaction retries and succeeds, will the below code run once?
// logic that requires the transaction succeeds
await axios.post(url, data);
res.status(200).send('success');
} catch (e) {
res.status(500).send('system error');
}
});
Appreciate expert views on this. Thanks
You can find the documentation for runTransaction here.
As you can see, runTransaction() returns a Promise. When you await a Promise, and with you code inserted in a try/catch block, if an error is thrown everything after will be ignored, because the flow will go in the catch statement.
So the answer is yes: everything after runTransaction(), the way you wrote it, will not be executed if something goes wrong.

Is return value important in Firebase Cloud Functions

I am writing the Firebase Could Functions with TypeScript and the following is a simple method to update a document.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp(functions.config().firebase);
export const handleTestData = functions.firestore.document('test/{docID}').onCreate(async (snap, context) => {
const data = snap.data();
if (data) {
try {
await admin.firestore().doc('test1/' + context.params.docID + '/').update({duplicate : true});
} catch (error) {}
}
});
In this method, the promise is handled by async await and there is no return statement and it's working fine. Most of the examples/tutorials I have seen always have a return statement in each method.
Is there any impact/difference I don't return anything in Firebase Cloud Functions? If I should return something, can I return null?
Is return value important in Firebase Cloud Functions?
Yes, it is really key, in a Cloud Function which performs asynchronous processing (also known as "background functions") to return a JavaScript promise when all the asynchronous processing is complete, as explained in the documentation.
Doing so is important for two main reasons (excerpts from the doc):
You make sure that the Cloud Functions instance running your Cloud Function does not shut down before your function successfully reaches its terminating condition or state.
You can avoid excessive charges from Cloud Functions that run for too long or loop infinitely.
Why is your Cloud Function running correctly even if you don't return a Promise?
Normally your Cloud Function should be terminated before the asynchronous operations are completed, because you don't return a Promise and therefore indicate to the Cloud Functions platform that it can terminate the Cloud Functions instance running the Cloud Function.
But sometimes, the Cloud Functions platform does not terminate the Function immediately and the asynchronous operations can be completed. This is not at all guaranteed and totally out of your control.
Experience has shown that for short asynchronous operations this last case happens quite often and the developer thinks that everything is ok. But, all of sudden, one day, the Cloud Function does not work... and sometimes it does work: The developer is facing an "erratic" behaviour without any clear logic, making things very difficult to debug. You will find a lot of questions in Stack Overflow that illustrate this situation.
So concretely, in your case you can adapt your code like:
export const handleTestData = functions.firestore.document('test/{docID}').onCreate(async (snap, context) => {
const data = snap.data();
if (data) {
try {
// See the return below: we return the Promise returned by update()
return admin.firestore().doc('test1/' + context.params.docID + '/').update({duplicate : true});
} catch (error) {
return null; // <- See the return
}
} else {
return null; // <- See the return
}
});
or like
export const handleTestData = functions.firestore.document('test/{docID}').onCreate(async (snap, context) => {
const data = snap.data();
if (data) {
try {
await admin.firestore().doc('test1/' + context.params.docID + '/').update({duplicate : true});
return null; // <- See the return
} catch (error) {
return null; // <- See the return
}
} else {
return null; // <- See the return
}
});
Returning null (or true, or 1...) is valid since an async function always returns a Promise.

how to run firebase query inside a function in node js

I have a function which should query firebase db and return a result.
function verifyToken(token)
{
var androidId = 'xxxxx';
admin.database(dbDEV).ref('profiles').orderByChild('androidId').equalTo(androidId).on('value',(snapshot)=>{
console.log(snapshot.val());
return snapshot.val();
});
}
I am using firebase functions for this . so the result is getting logged in firebase logs but i am not getting and return value while executing the function.
Two things:
Use once() instead of on() to query data a single time. on() establishes a listener that listens forever, until you remove the listener.
Realtime Database queries are all asynchronous, meaning they return immediately, and the callback function you provide is invoked some time later with the results. You can't simply return the results from the callback in order to return those results from the enclosing function. If you want verifyToken to yield query results to the caller, you should return a promise that resolves with the data.
You can start by using promises. For example:
function verifyToken(token) {
return new Promise(resolve => {
var androidId = 'xxxxx';
admin.database(dbDEV).ref('profiles').orderByChild('androidId').equalTo(androidId).on('value',(snapshot)=>{
console.log(snapshot.val());
resolve(snapshot.val());
});
});
}
And when you need the result:
verifyToken(token).then(result => {
... do stuff
});
To improve on this, you can use an async function. For example:
async function foo() {
const result = await verifyToken(token);
console.log(result);
}

How to listen for specific Firestore document creation event?

I am implementing a command/response pattern where the user writes to a command collection by calling add with a payload under his own userId, and then gets the data from a similar response path.
However the code below doesn't work, because onSnapshot can not listen for a document that hasn't yet been created (document command.id in the /responses/{userId}/register collection). This would be easy to solve with an onCreate handler, which exists for cloud functions but not for the JS firebase client API it seems.
This is using redux-firestore and some of my app helper functions, but you'll get the idea. The command and response document structures use { payload, error} similar to FSA
Cloud Function
export const register = functions.firestore
.document("commands/{userId}/register/{commandId}")
.onCreate(async event => {
const payload = event.data.get("payload");
const { userId, commandId } = event.params;
const response = db.document(`responses/${userId}/register/${commandId}`)
// possibly something bad will happen
try {
// do something with payload...
return response.set({
payload: "ok" // or pass relevant response data
})
} catch(err) {
return response.set({
error: true
payload: error
})
}
});
Client
export async function register(fs: any, userId: string) {
try {
// issue a new command
const command = await fs.add(
{ collection: `commands/${userId}/register` },
{ payload: fs.firestore.FieldValue.serverTimestamp() }
);
// wait for the response to be created
fs.onSnapshot(
{ collection: `responses/${userId}/register`, doc: command.id },
function onNext(doc) {
const {error, payload} = doc.data()
if (error) {
return notify.error({ title: 'Failed to register', message: payload.message });
}
notify.json(payload);
},
function onError(err) {
notify.error(err);
}
);
} catch (err) {
notify.error(err);
}
}
Is there no such thing as onCreate for web clients?
The only scalable solution I can think of is to store the response data as a child in the command document, but I think it is not as nice, because I suspect you can not make the permissions as strict then.
I would like the user only to be able to write to the command, and only read from the response paths. If I place the response as a child of command, this would not be possible I think?
I'm wondering if I'm not overlooking some API...

Redux - Jest: Testing functions that have void return

New to Jest and Redux and I'm having trouble with testing functions that are dispatching to the store but don't yield a return value. I'm trying to follow the example from the Redux website does this
return store.dispatch(actions.fetchTodos()).then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions)
})
however I have several "fetchtodos" functions that don't return anything which causes the error TypeError:
Cannot read property 'then' of undefined due to returning undefined
I'm wondering what I can do to test that my mock store is correctly updating. Is there a way to dispatch the function, wait for it to finish and then compare the mock store with expected results?
Thanks
Edit: We're using typescript
action from tsx
export function selectTopic(topic: Topic | undefined): (dispatch: Redux.Dispatch<TopicState>) => void {
return (dispatch: Redux.Dispatch<TopicState>): void => {
dispatch({
type: SELECT_Topic,
payload: topic,
});
dispatch(reset(topic));
};
}
test.tsx
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('Select Topic action', () => {
it('should create an action to select .', () => {
const topic: Topic = mockdata.example[0];
const expectedAction = {
type: actions.SELECT_TOPIC,
payload: topic,
};
const store = mockStore(mockdata.defaultState);
return store.dispatch(actions.selectTopic(topic)).then(() => {
expect(store.getState()).toEqual(expectedAction);
});
});
});
The action is what I'm given to test(and there are many other functions similar to it. I'm getting that undefined error when running the test code, as the function isn't returning anything.
In Redux, the store's dispatch method is synchronous unless you attach middleware that changes that behavior, ie: returns a promise.
So this is likely a redux configuration problem. Be sure you are setting up your test store with the same middleware that allows you to use the promise pattern in production.
And as always, be sure to mock any network requests to avoid making api calls in test.

Resources