永遠に終わらない位置情報...
Expo SDKのexpo-locationにあるgetCurrentPositionAsyncには位置情報の取得にハングアップするバグがあります。
これはissuesにも挙げられていて、すぐに応答することもあれば、ハングアップすることもあります。
iOS or Androidの両方で起きており、しかも発生タイミングに規則性がなくランダムに起きる不思議なバグです。
解決方法
ここのissuesにもありますが、timeoutとretryロジックを組み合わせて暫定対応をすることができます。
なぜtimeoutとretryをするのか?
上記にも触れた通り、このバグは発生タイミングに規則性がないので、何回か再実行をすると位置情報を取得することができます。
なので、timeoutする秒数とretryする回数を決めておき、そのretry回数内で位置情報を取得しようって方法です。
例
位置情報の取得に許容できる合計時間が10秒の場合
timeout = 2000ms
retry = 5回
実装
issuesのプログラムコードを参考に作ってみました。
注意
本コードはあくまでもサンプルです。
動作を保証するものではありません。
timeout関数
// 指定した時間内に処理が終わらなかった場合はタイムアウトエラーをthrowします。
export async function timeoutAsync<T = any>(onTry: () => Promise<T>, time: number): Promise<T> {
return new Promise((resolve, reject) => {
// タイムアウト処理
const timeout = setTimeout(() => {
reject(new Error('タイムアウト'));
}, time);
// 処理を実行
void onTry()
.then((result) => {
clearTimeout(timeout);
resolve(result);
})
.catch((err) => {
throw new Error(err);
});
});
}
retry関数
type Response<T> = {
retryCount: number;
result: T;
};
// 成功するまで指定した回数分だけ再実行します。
export async function retryAsync<T = any>(maxRetryCount: number, onTry: () => Promise<T>): Promise<Response<T | null>> {
let count = 0;
let result: T | null = null;
while (count < maxRetryCount) {
try {
result = await onTry();
break;
} catch {
result = null;
} finally {
count += 1;
}
}
return { retryCount: count, result };
}
getCurrentPositionAsyncをwrapする
import { getCurrentPositionAsync, LocationOptions } from 'expo-location';
type Args = {
timeout?: number
locationOptions?: LocationOptions
}
export async function getLocationCurrentPositionAsync(args?: Args) {
const { timeout = 2000, locationOptions } = args ?? {}
const { result, retryCount } = await retryAsync<LocationObject | null>(5, () =>
timeoutAsync(() => getCurrentPositionAsync(locationOptions), timeout)
);
return { result, retryCount };
}
こうすることで、MAX10秒として位置情報を取得することが可能になります。
もし10秒待っても取得できなかった場合はresultがnullになるので、Alertを出すなりしてもう一度ユーザーに実行してもらうように誘導することも可能です。
ちなみにretryCountをreturnしてるのはログを残して調査に使用したりするためにあるだけなので、不要ならretrunする必要はないです。