I am basically trying to wrap the React Native GPS feature into a Promise.
When I reach the failure callback of navigator.geolocation.getCurrentPosition I want to manually reject a Promise.
Here the relevant code:
var p = new Promise();
navigator.geolocation.getCurrentPosition(
(position) => { ... all good here ... },
(error) => {
// trying to reject manually a Promise
p.reject(error);
}
);
Problems:
If I don't pass any function in the Promise arguments I get not a function error on the first line
If I pass a function I get undefined is not a function evaluating p.reject
I was confusing Promise with an Angular-like deferred promise.
A working way is to wrap the GPS function into a Promise like so:
return new Promise(function(resolve, reject) {
navigator.geolocation.getCurrentPosition(
(position) => {
resolve('All good');
},
(error) => {
reject(error);
}
);
});
Related
I am building my first react native application and I am using DeviceInfo (react-native-device-info) and geolocation API.
DeviceInfo.getMACAddress(mac => console.log(mac);
and
navigator.geolocation.getCurrentPosition(position => console.log(position)
both of them are returning promise. How to merge them with something like promise.all()
Well, I studied your code but what you're doing isn't called a promise, you're objectively giving a callback to the function instead of using .then to resolve the promise.
Check the API of GetMacAddress here also the getCurrentPosition doesn't use promises, it uses callbacks instead, it's api is here.
What you can do to use Promise.all is that, you can wrap getCurrentPosition to a promise and then use promise.all with the DeviceInfo.
wrap getposition to a promise:
const getPosition = (options) => {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, options);
});
}
now use Promise.all like this:
const getData = async () => {
const [macAddress, currentPos] = await Promise.all([
DeviceInfo.getMACAddress(),
getPosition(),
]);
// use macAddress and currentPosition here.
}
Now the macAddress and currentPos are the outputs of the getMACAddress and getCurrentPosition function.
If you're not using an async function, then you can do the following:
Promise.all([
DeviceInfo.getMACAddress(),
getPosition(),
]).then((macAddress, pos) => {
// access macAddress and pos in this func
}).catch((error) => {
// access any error here
})
In my project, I use RxJS to handle HTTP request. I came into a confusing point about the error handling part as following:
.switchMap((evt: any) => {
return http.getComments(evt.params)
.map(data => ({ loading: false, data }))
.catch(() => {
console.log('debugging here');
return Observable.empty();
});
})
in the above code, inside the switchMap operator, I use the http.getComments function to send request, which is defined by myself as following:
getComments(params) {
return Observable.fromPromise(
this.io.get(path, { params })
);
}
in this function, I use fromPromise operator convert the returned Promise to observable.
The problem is when HTTP request failed, the catch operator inside switchMap can not work, the debugging console can't output. So what's wrong with my code.
Do you really need to catch the error inside the switchMap anyway? You can handle your error in your source if you want.
.switchMap((evt: any) =>
http.getComments(evt.params).map(data => ({ loading: false, data }))
})
.subscribe(console.log, console.error);
Any way, your source code does not look to have any error, maybe your promise is not been rejected when the http fails and is just resolving with an error as a response (this is a guess because I've seen that before)
Your code should work. Here a simplified simulation, where http calls are substituted by a function which raises an error.
import {Observable} from 'rxjs';
function getComments(params) {
return Observable.throw(params);
}
const params = 'abc';
Observable.of(null)
.switchMap(_ => {
return getComments(params)
.map(data => ({ loading: false, data }))
.catch(err => {
console.log('debugging here', err);
return Observable.empty();
});
})
.subscribe(
console.log,
error => console.error('This method is not called since the error is already caught', error),
() => console.log('DONE')
)
export const USER_KEY = "isLoggedIn";
export const phoneVerified = () => AsyncStorage.setItem(USER_KEY, 1);
export const userInfoVerified = () => AsyncStorage.setItem(USER_KEY, 2);
I have used the above functions to store the value and the below one to get the value.
export const isSignedIn = () => {
return new Promise((resolve, reject) => {
AsyncStorage.getItem(USER_KEY)
.then(res => {
console.log("from isSignedIn : "+res); //res is showing null.
if (res !== null) {
resolve(res);
} else {
resolve(0);
}
})
.catch(err => reject(err));
});
};
Why this always returns null? I was trying async/await but still getting null. I think somehow the data is not storing.
I'm afraid you can only store strings. Please refer to this React Native AsyncStorage storing values other than strings and this https://facebook.github.io/react-native/docs/asyncstorage.html#setitem
Thanks.
As answered by #Vishu Bhardwaj AsyncStorage accepts only string. So you can use JSON.stringify() and JSON.parse() in such cases.
I was stuck with this stupid problem for almost one week, no other way that is suggested in all communities worked for me, but then I found something that is built of react-native which its setState() callback function: https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296.
so the only way that I guarantee that it's the only secure way so far is this that u use the setState() function in your promise and everything that you need to run, put them on a function and call it for the setState() callback function , this is only way you can make sure yourself that you neither get null nor never calling the function . Here I'm going to provide an example of it which this.tokeToServer() is my function which it's used as a callback function.
try {
AsyncStorage.getItem('firebase_token',(err,item) => {
if (item) {
this.setState({
firebase_token: item,
}),this.tokenToServer();
}
});
} catch (error) {
console.log("Error retrieving data" + error);
}
As presented by friend Abdu4, I had the same problem for 4 days and searching for different sites and forums. Attempts with async/await and others, even though you should use these options, the one you completed and really worked was to assign the value through setState by callback
try {
AsyncStorage.getItem('TOKEN_KEY',(err,item) => {
if (item) {
setToken({
Token: item,
});
}
});
} catch (error) {
console.log("Error retrieving data" + error);
}
I'm trying to use react native Geolocation to getCurrentPosition and then as soon as the position is returned, use react native geocoder to use that position to get the location. I'm using redux-observable epics to get all of this done.
Here are my two epics:
location.epic.js
import { updateRegion } from '../map/map.action'
import Geocoder from 'react-native-geocoder'
export const getCurrentLocationEpic = action$ =>
action$.ofType(GET_CURRENT_LOCATION)
.mergeMap(() =>
Observable.fromPromise(Geocoder.geocodePosition(makeSelectLocation()))
.flatMap((response) => Observable.of(
getCurrentLocationFulfilled(response)
))
.catch(error => Observable.of(getCurrentLocationRejected(error)))
)
export const getCurrentPositionEpic = action$ =>
action$.ofType(GET_CURRENT_POSITION)
.mergeMap(() =>
navigator.geolocation.getCurrentPosition(
(position) => Observable.of(
updateRegion(position),
getCurrentLocation(position)
),
error => Observable.of(getCurrentPositionRejected(error)),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
).do(x => console.log(x))
).do(x => console.log(x))
As soon as the app starts, this code executes:
class Vepo extends Component {
componentDidMount() {
const { store } = this.context
this.unsubscribe = store.subscribe(() => { })
store.dispatch(fetchCategories())
store.dispatch(getCurrentPosition())
}
fetchCategories() is an action that has an epic too, but that is working. dispatching the getCurrentPosition() action runs through the epic above. The only output that I can see is that my reducer handles getLocationRejected() as it console logs this:
there was an issue getting your current location: Error: invalid position: {lat, lng} required
at Object.geocodePosition (geocoder.js:15)
at MergeMapSubscriber.project (location.epic.js:17)
at MergeMapSubscriber._tryNext (mergeMap.js:120)
at MergeMapSubscriber._next (mergeMap.js:110)
at MergeMapSubscriber.Subscriber.next (Subscriber.js:89)
at FilterSubscriber._next (filter.js:88)
at FilterSubscriber.Subscriber.next (Subscriber.js:89)
at Subject.next (Subject.js:55)
at Object.dispatch (createEpicMiddleware.js:72)
at Object.dispatch (devTools.js:313)
Here is my reducer:
const searchPage = (
initialLocationState = initialState.get('searchForm').get('location'),
action: Object): string => {
switch (action.type) {
case GET_CURRENT_LOCATION_FULFILLED: {
return action.payload
}
case GET_CURRENT_LOCATION_REJECTED: {
console.log('there was an issue getting your current location: ',
action.payload)
return initialLocationState
}
case GET_CURRENT_POSITION_REJECTED: {
console.log('there was an issue getting your current position: ',
action.payload)
return initialLocationState
}
default:
return initialLocationState
}
}
Is there anything obvious I am doing wrong? My attempt to debug by adding .do(x => console.log(x)) does nothing, nothing is logged to the console. updateRegion() never does fire off because that dispatches an action and the reducer UPDATE_REGION never executes. But the execution must make it into the success case of getCurrentPosition() eg:
(position) => Observable.of(
updateRegion(position),
getCurrentLocation(position)
),
must execute because the getCurrentLocation(position) does get dispatched.
Where am I going wrong?
What would be your technique for using an epic on a function which takes a callback function? getCurrentPosition() takes a callback and the callback handles the payload. Basically if you remove Observable.of( from inside getCurrentPosition(), that's how getCurrentPosition() is correctly used - and has been working for me without redux-observable.
Wrapping anything in a custom Observable is fairly simple, very similar to creating a Promise except Observables are lazy--this is important to understand! RxJS Docs
In the case of geolocation, there are two main APIs, getCurrentPosition and watchPosition. They have identical semantics except that watchPosition will call your success callback every time the location changes, not just a single time. Let's use that one since it's natural to model it as a stream/Observable and most flexible.
function geolocationObservable(options) {
return new Observable(observer => {
// This function is called when someone subscribes.
const id = navigator.geolocation.watchPosition(
(position) => {
observer.next(position);
},
error => {
observer.error(error);
},
options
);
// Our teardown function. Will be called if they unsubscribe
return () => {
navigator.geolocation.clearWatch(id);
};
});
}
geolocationObservable({ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 })
.subscribe(
position => console.log(position),
e => console.error(e)
);
// will log every time your location changes, until you unsubscribe
Since it's now an Observable, if you only want the current location you can just do .take(1).
So using it inside your epic might be like this
// If you want, you could also use .share() to share a single
// underlying `watchPosition` subscription aka multicast, but
// that's outside the scope of the question so I don't include it
const currentPosition$ = geolocationObservable({
enableHighAccuracy: true,
timeout: 20000,
maximumAge: 1000
});
export const getCurrentPositionEpic = action$ =>
action$.ofType(GET_CURRENT_POSITION)
.mergeMap(() =>
currentPosition$
.take(1) // <----------------------------- only the current position
.mergeMap(position => Observable.of(
updateRegion(position),
getCurrentLocation(position)
))
.catch(error => Observable.of(
getCurrentPositionRejected(error)
))
);
As a side note, you might not need to dispatch both updateRegion() and getCurrentLocation(). Could your reducers just listen for a single action instead, since they both seem to be signalling the same intent?
I have made API on restify for my product. I have installed loggly and newrelic for monitoring purposes.
Recently newrelic notified me that there was an error somewhere in my code.
app.get('/myroute', (req, res, next) => {
let payload = { /*...*/ };
return new Promise((resolve, reject) => {
/* some code */
return resolve();
})
.then(/* some promise */)
.then(() => {
/* some code */
sendEmail({params}); // some error was thrown from one of the promise chain
// inside this function...
return something;
})
.then(/* some promise */)
.then(() => {
res.send(something);
})
.catch(next);
});
The promise chain resolved perfectly fine and user gets proper response, because I didn't return the sendEmail function. And I don't want to, because I don't want the user to wait for the response for long.
function sendMail(params) {
return new Promise((resolve, reject) => {
/* some code */
return resolve();
})
.then(() => {
params.abc.replace('a', 'b'); // error thrown, cannot find replace of undefined.
return something;
});
}
newrelic caught the error, but loggly didn't. I already have restify's formatters setup but it won't be enough as the request was successfully served and didn't fall into .catch(next).
I don't want a solution to fix the sendMail function. I just want to log if there is a bug like this.
Is there anyway to catch and log this type of error without putting a .catch() to sendMail function?
I figured out. I was trying process.on('uncaughtException') to handled it.
But I came across process.on('unhandledRejection') which did the job!
https://nodejs.org/api/process.html#process_event_unhandledrejection