I'm working on mobile app development using React Native and Expo. I have a promise which calls GeoFirestore to query data from firebase firestore by distance order, and my app sometimes crashes because the GeoFirestore query doesn't finish.
So I need to add a timeout to the heavy promise, and I tried to use Promise.race with two promises, a promise that calls GeoFirestore API, and another promise that rejects after a given time using SetTimeout, like the below code (put infinite loop instead of actual GeoFirestore call so that it is executable).
However, I found that setTimeout doesn't reject even if the time passed longer than the given time.
How can we add a timeout to a heavy promise?
I guess that javascript runs on a single-thread and the heavy operation takes a thread but the timer task couldn't run...
const makeTimeoutPromise = (timeMs) => new Promise(function(resolve, reject) {
setTimeout(() => reject(`timeout`), timeMs);
});
const heavyOperationPromise = () => new Promise(function(resolve, reject) {
while (true) { } // a very heayy operation.
resolve();
});
const test = async () => {
try {
console.log('test started');
const result = await Promise.race([makeTimeoutPromise(100), heavyOperationPromise()]);
console.log(`done result=${result}`);
} catch (e) {
console.log(`catch e=${e}`);
}
}
test();
Update:
while(true) {}
seems not good example, which blocks a thread.
Here is actual code. GeoQuery.near is what I need to run in a promise and it takes too long or let app crash depending on center
, radius
and data in firestore DB.
import { GeoFirestore } from 'geofirestore'; // GeoFirestore 3.4.1
getUsersByDistance = async (location, radius) => {
try {
const geofirestore = new GeoFirestore(firebase.firestore());
const activeUsers = geofirestore.collection('users').where('userStatus', '==', 'active');
const query = activeUsers.near({
center: new firebase.firestore.GeoPoint(location.latitude, location.longitude), radius });
const makeTimeoutPromise = (timeMs) => new Promise(function(resolve, reject) {
setTimeout(() => reject('timeout'), timeMs);
});
// set timeout 5 seconds, but the setTimeout doesn't reject sometimes depending on the center, radius and data in firestore DB.
// query.get sometimes takes very long time, sometimes app crash.
const result = await Promise.race([makeTimeoutPromise(5000), query.get()]);
// Do something with result
} catch (e) {
console.log(e);
}
}
If the heavy operation is not
async
it won't give a chance for thesetTimeout
callback to execute from the event loop. If given a breathing space it will work for example.