KASHIMURA Blog

Webサービス開発のこと、個人的なことを書いているブログ

ブリッジの実装は気をつけないと永遠に処理が止まる #ReactNative

f:id:kasssssy:20200425164207p:plain

はじめに

ReactNativeでAndroid用のブリッジを実装する時、@ReactMethodを付与したメソッドにPromiseを渡している場合は、何か返さないと呼び出し元でawaitがついているときにずっと待ってしまいます。
new Promise() ではなく、普段async/awaitで非同期処理を書いていると結構やらかしてしまうかもしれません。

ということで、ブリッジでも非同期実装する時は気をつけましょうという記事です。
これだけだと分かりづらいので以下に簡単なサンプルを書きました。

環境

  • react native: 0.62.2
  • typescript: 3.8.3

サンプル

ブリッジの実装
(コードは公式のものをそのまま使ってます。)

呼び出し元の実装

解説

ToastModuleに定義したshow()メソッドは、トーストを表示するメソッドです。
引数にPromiseを渡しているので非同期なメソッドです。
ただ、何も返してません。

@ReactMethod
public void show(String message, int duration, Promise promise) { 
    Toast.makeText(getReactApplicationContext(), message, duration).show();
}

App.tsxでは、ボタンを押した時にトーストとアラートを表示するようにしています。
ボタン押下時に呼ばれるshow()メソッドの中の1行目でブリッジのメソッドを呼んでいます。

const show = async () => {
  await ToastExample.show('Hello!', ToastExample.SHORT);
  Alert.alert('Hello^^');
};

トーストもアラートも表示されそうですが、アラートは表示されません。
理由は、ToastExample.show()にはawaitを付けているので何か返ってくるのを待つことになります。
ただ、ToastExample.show()は中で何も返していないので、呼び出し元で永遠に待つことになり処理が止まってしまうからです。

実際に動かしてみるとこんな感じです。
アラートが表示されていないのが分かると思います。

f:id:kasssssy:20200425155842g:plain

解決策

  • 非同期なメソッドである必要性を考え直す
  • promise.resolve()/promise.reject() で何か返す

さいごに

これは、エラーも何も吐かずただ処理が止まってしまうので厄介です。
ま、理解していれば、なんてことないことですね。

最後まで読んで頂きありがとうございます。

GitHubに上げたサンプルコード

参考

Native Modules · React Native