32
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

アイレット株式会社Advent Calendar 2024

Day 1

【Expo】getCurrentPositionAsyncのハングアップバグに対応する

Last updated at Posted at 2024-12-13

永遠に終わらない位置情報...

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する必要はないです。

32
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?