Code not executing when awaiting dispatch in for loop - react-native

I can not find anything on this so I decided to ask it here.
I am using react-native and redux.
I left the business logic out of the code.
I have this function which calls another function that does an API call.
The function that does the API call needs to access "getState", that's the reason I use dispatch.
const getStuff = (day) => (dispatch) => {
const result = await dispatch(functionThatDoesAPIRequest(day)
}
Then I have this code that loops over days and in some cases needs to do the API call. In the case I was testing it does not need to access the API. I could not find the problem with my code until I placed the line between the if statement in comments. Then I changed the if to always be False and the error persisted when the line of code was not in comments, even though it could never reach that piece of code.
for (let i = 0; i < dayArray.length; i+=1) {
if (false) {
const resultThatIsNotNeeded = await dispatch(getStuff(dayArray[i])
}
// more business logic
}
Changing the for loop to a ForEach loop fixed this issue somehow. But I really have no clue why this fixed it.
dayArray.forEach(async (day, i) => {
if (false) {
const resultThatIsNotNeeded = await dispatch(getStuff(dayArray[i])
}
}

That is because on your forEach the await call is inside of a async function as it should be
On the for loop I don't see any of this code being executed within a async function

Related

React Native Expo Task Manager

Eventually, I would like to be able to run background tasks in my React Native app (Axios fetch to get some fresh data at least once a day). I am struggling to make this work:
https://docs.expo.io/versions/latest/sdk/task-manager/
import * as BackgroundFetch from 'expo-background-fetch';
import * as TaskManager from 'expo-task-manager';
const FETCH_TASKNAME = 'test_task'
const INTERVAL = 60
function test() {
console.log('function is running')
}
export async function registerFetchTask() {
TaskManager.defineTask(FETCH_TASKNAME, test());
const status = await BackgroundFetch.getStatusAsync();
switch (status) {
case BackgroundFetch.Status.Restricted:
case BackgroundFetch.Status.Denied:
console.log("Background execution is disabled");
return;
default: {
console.debug("Background execution allowed");
let tasks = await TaskManager.getRegisteredTasksAsync();
if (tasks.find(f => f.taskName === FETCH_TASKNAME) == null) {
console.log("Registering task");
await BackgroundFetch.registerTaskAsync(FETCH_TASKNAME);
tasks = await TaskManager.getRegisteredTasksAsync();
console.debug("Registered tasks", tasks);
} else {
console.log(`Task ${FETCH_TASKNAME} already registered, skipping`);
}
console.log("Setting interval to", INTERVAL);
await BackgroundFetch.setMinimumIntervalAsync(INTERVAL);
}
}
}
and calling this in App.js
import { registerFetchTask } from './helpers/backgroundFetch'
registerFetchTask();
I am getting a console logs up to this point:
function is running
Background execution allowed
Registering task
But I am unfortunately also getting following errors:
TaskManager.defineTask must be called during the initialization phase!
I read also in the documentation and as per example code, I am running in App.js directly and not in the component class.
And I am getting the following warning:
[Unhandled promise rejection: Error: Task 'test_task' is not defined. You must define a task using TaskManager.defineTask before registering.]
Which I don't understand since it is defined at the very top and before registering.
It is only unfortunate there is no proper working example anywhere to be found. An example would save countless hours for people struggling with this. Is there maybe an easier way of making background tasks running in react native apps?
Thanks so much for your kind help.
I can spot a couple of problems:
When you define the task, pass in a reference to the function rather than calling it:
TaskManager.defineTask(FETCH_TASKNAME, test); // no parentheses
registerFetchTask() is async which means it returns a promise when called in App.js. That probably doesn't count as the "initialization phase" of the app so try removing async
I don't know whether these changes will solve the problem but they should help.
change the line TaskManager.defineTask(FETCH_TASKNAME, test()); to
TaskManager.defineTask(FETCH_TASKNAME, test);
instead of passing the return value of the function pass the function reference.

How to correctly utilise redux-saga putResolve

I'm trying to sequence my API calls so that I can call an endpoint, wait for it to succeed, then select the required value out of the state.
I'm very new to redux-saga and am finding it difficult to understand how to achieve this!
What I've tried:
So we try and block whilst we perform an API call:
const resp = yield putResolve(assistantActions.fetchSustainability());
Later in the code we select the updated value out of the state:
const assistant = yield select(getAssistant);
The action that is called looks like:
export const fetchSustainability = () => ({ type: FETCH_SUSTAINABILITY });
In the saga we:
yield takeEvery(FETCH_SUSTAINABILITY, fetchSustainability);
Then the function returns a promise:
function fetchSustainability() {
return axios.get(`${url}/sustainability`);
}
I'm not sure that I've set this up any where near right as I've made lots of changes trying to get it working!
The select statement executes immediately and as the call isn't blocked the state isn't updated correctly.
Is my approach correct? If not is someone able to provide a simple working example on how to achieve a blocking call.
So the second parameter to takeEvery needs to be a generator function;
within that you can call your fetchSustainability
function* generator() {
yield call(fetchSustainability); // calls the actual function
}
// in the saga
yield takeEvery(FETCH_SUSTAINABILITY, generator);
your action fetchSustainability should return a Promise.
Here's an example to highlight the difference between put and putResolve.
https://codesandbox.io/s/redux-saga-example-x77jb
The example uses thunkMiddleware

RN with Firestore unable to wait for Promise to resolve

I have a simple call to Firestore to write a doc and then wait for the doc to finish writing before changing state of the parent. However, the parent state is being changed too fast, resulting in reading fields that I think have not yet been written/propagated. I tried adding a delay with setTimeout and it seems ignored. How can I make sure the state change is absolutely only called after the Firestore doc is written completely?
The code:
updateDBEntry(stateObj) {
var that = this;
var docRef = firebase.firestore().collection('sessions').doc(this.state.userID);
docRef.get().then((doc) => {
if (!doc.exists) {
const timestamp = firebase.firestore.FieldValue.serverTimestamp();
var duration = (stateObj.seshDuration) ? stateObj.seshDuration : 1;
docRef.set({
seshName: stateObj.seshName,
seshStreet: stateObj.seshStreet,
seshZipcode: stateObj.seshZipcode,
seshDuration: duration,
seshDesc: stateObj.seshDesc,
seshTime: timestamp,
}).then(() => {
var handleToUpdate = that.props.handleToUpdate;
setTimeout(() => {
handleToUpdate(1); //this changes the parent's state
}, 10000);
});
}
});
}
I'm not sure exactly the problem you're running into here, mostly because you've only shown this one function, and not how you're using it in the rest of your app. But I can tell you three things for sure:
When the promise from set() resolves successfully, you can be certain the document is written.
get() and set() are asynchronous, and so is then(). They all return promises the represent async work.
Item 2 means that your entire function updateDBEntry() is also asynchronous and returns immediately, before any of the work is complete.
Because this entire function is async, when it returns, the document will not have been created yet. Instead, maybe this function should return that resolves only after all the work is complete, so that the caller can also use it set up some code to execute after the work is done.

promise chaining vs promise.all

i have a task to enter the notification date using protractor
where i need to clear contents before entering so i have came up with this code
this.Then(/^I should enter "Notification Date"$/, () => {
const d = new Date();
return orderCheckOutPage.pageElements.recipientNotificationDateMonth.clear().then(() => {
return orderCheckOutPage.pageElements.recipientNotificationDateMonth.sendKeys(d.getMonth() + 1).then(() => {
return orderCheckOutPage.pageElements.recipientNotificationDateDay.clear().then(() => {
return orderCheckOutPage.pageElements.recipientNotificationDateDay.sendKeys(d.getDate()).then(() => {
return orderCheckOutPage.pageElements.recipientNotificationDateYear.clear().then(() => {
return orderCheckOutPage.pageElements.recipientNotificationDateYear.sendKeys(d.getFullYear())
})
})
})
})
})
});
my friend told me the above code be can refactored as
const promises = [];
promises.push(orderCheckOutPage.pageElements.recipientNotificationDateMonth.clear());
promises.push(orderCheckOutPage.pageElements.recipientNotificationDateMonth.sendKeys(d.getMonth() + 1));
promises.push(orderCheckOutPage.pageElements.recipientNotificationDateDay.clear());
promises.push(orderCheckOutPage.pageElements.recipientNotificationDateDay.sendKeys(d.getDate()));
promises.push(orderCheckOutPage.pageElements.recipientNotificationDateYear.clear());
promises.push(orderCheckOutPage.pageElements.recipientNotificationDateYear.sendKeys(d.getFullYear()));
return promise.all(promises);
I heard promise.all will start resolving promises one by one
first it would go to first statement and try to resolve if it [here in the above case is clear month] if is asynch the it would jump to second one and try to execute the statement [sending keys to month]
here both the tasks of clearing and entering will be run parallely
and tasks are executed based on the time promises get resolved
if it is that case will there be chances of sendkeys being exeuted before clearing
correct me if i am wrong...!!!!!
Protractor has its own promise manage mechanism called control flow, To understand control flow simply, you can think it is a queue.
When nodejs execute your Protractor script line by line, if the expression in the line return a promise, control flow will add the promise into the queue.
After all lines execute done, you will get a promise queue, at this time point your testing had not finish yet, because control flow will make your testing to wait all promise in the queue be executed. Now control flow will pop a promise from the queue and execute and wait it complete, then next promise.
So with such mechanism, your script can be executed as the order as you write
down in file. Actually what control flow did is more complex than I said here.
You no need to use nested then chain in your case, your code like the callback pyramid, not represent the advantage of promise (promise is to resolve callback pyramid). Your code can be simple as below:
const d = new Date();
//input month
orderCheckOutPage.pageElements.recipientNotificationDateMonth.clear();
orderCheckOutPage.pageElements.recipientNotificationDateMonth.sendKeys(d.getMonth() + 1);
//input day
orderCheckOutPage.pageElements.recipientNotificationDateDay.clear();
orderCheckOutPage.pageElements.recipientNotificationDateDay.sendKeys(d.getDate());
//input year
orderCheckOutPage.pageElements.recipientNotificationDateYear.clear();
orderCheckOutPage.pageElements.recipientNotificationDateYear.sendKeys(d.getFullYear());
For your case, no need to use promise.all(), because all interaction of your code not to get some value from page. I will give an example to help you learn in which case it's better to use promise.all():
Assume i have a page and it display a price and a amount. I need to calculate the fee by price * amount.
Use nested then chain:
var fee = ele_price.getText().then(function(price){
return ele_amount.getText().then(function(amount){
return price * amount;
});
});
fee.then(function(fee){
console.log(fee);
});
Use promise.all():
var fee = promise.all([
ele_price.getText(),
ele_amount.getText()
])
.then(function(datas){
var price = datas[0];
var amount = datas[1];
return price * amount;
});
fee.then(function(fee){
console.log(fee);
});
So use promise.all(), one then() is enough. This makes your code more readable than nested then chain.
Hope you now understand why no need to use promise.all() in your case.

CameraCaptureUI.captureFileAsync fails to return IAsyncOperation object

For some reason, my code is unable to retrieve the IAsyncOperation object that is returned upon calling captureFileAsync method of the Windows.Media.Capture.CameraCaptureUI() method. The IAsyncOperation object is returned according to this documentation. In that documentation link, it states:
Return value
Type: IAsyncOperation<StorageFile>
When this operationcompletes, a StorageFile object is returned.
So here is my code:
var dialog = new Windows.Media.Capture.CameraCaptureUI();
var aspectRatio = { width: 4, height: 3 };
dialog.photoSettings.croppedAspectRatio = aspectRatio;
appSession.InAsyncMode = dialog.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo).done(function (file) {
if (file) {
self.addPage(URL.createObjectURL(file));
} else {
WinJS.log && WinJS.log("No photo captured.", "sample", "status");
}
}, function (err) {
// None taken
});
When I inspect the value of appSession.InAysncMode, I see that the function returns undefined. I suspect it returns undefined because the operation is not complete (i.e. the user has not yet created the photo, and it has not been saved to disc), but I need it in order to cancel out of the camera capture mode programmatically. Does anybody know why it would return undefined instead of the documented IAsyncOperation object?
Thanks!
For reference, here's the answer I posted on the MSDN forum.
To answer your ending question, you can cancel the capture UI by canceling the promise from dialog.captureFileAsync.
Your InAsyncMode flag is undefined because you're assigning to it the return value from captureFileAsync.done() which is, by definition, undefined. It has nothing to do with the API's success.
In the docs, when you see IAsyncOperation, what you get in JavaScript is a promise that will deliver as a result to the completed handler if it succeed. You never see IAsyncOperation or related interfaces in JavaScript directly. The documentation for WinRT is written to be language-neutral, so it's important to understand how those things show up in JS (as promises). In C# you don't see it either, as you just use the await keyword. It's mostly in C++ that you actually encounter the interface.
Anyway, you I believe you want is something along the lines of the code below, where you could eliminate IsAsyncMode in favor of just checking for a non-null promise:
appSession.capturePromise = dialog.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo);
appSession.IsAsyncMode = (appSession.capturePromise != null);
//This will close the capture UI after 5 seconds--replace with whatever logic you need
setTimeout(function () { appSession.capturePromise.cancel(); }, 5000);
appSession.capturePromise.done(function (file) {
if (file) {
} else {
}
}, function (err) {
appSession.IsAsyncMode = false;
appSession.capturePromise = null;
});