JavaScriptの処理ロジック(ロジックの構築)に
お知恵をお借りしたいと思います。
【実現したいこと】
コード内の①と②に ①⇒②の処理順を設定したい。
ただし、①は実行されるとは限らない。
②は必ず実行されるが、①よりも後に実行され、
①で値がセットされていれば、①の値を使って②を処理したい。
【困っていること】
②は必ず起動しなければいけません。
現状、このコードを実行すると、②⇒①の順で実行されてしまうため、
①⇒②となるように、何らかのロジックを入れて制御したいです。
上記で記載したように ① が実行されるかわからないため、
なんらかの形で②の起動条件を決めないといけないかな、と思っています。
【コードの説明】
これはスマホアプリの挙動を制御するプログラムです。
ons.ready(function() {});
アプリが起動したら必ず実行される内容を記述する部分と思ってください。
(アイコンから起動した場合でも、プッシュ通知から起動した場合でも)
②init(mode)
アプリが起動したら実行される処理が記載されていると思ってください。
(アイコンから起動した場合でも、プッシュ通知から起動した場合でも)
①window.FirebasePlugin.onNotificationOpen
アプリが「プッシュ通知」から開かれた場合のみ実行される部分です。
そのため「アイコン」から起動された場合は実行されません。
【追記ここから】
なお、①のwindow.FirebasePlugin は Cordova という
アプリ開発環境が前提です(だと思っている)ので
ons.ready の中に置く必要があります。
※外に関数で定義して、ons.readyの中から外の関数を呼ぶでもOKです。
②のinit()は ons.readyの外にあっても処理自体は動きますが、
どこかで呼び出し、①⇒②の順でなければなりません・・・。
【追記ここまで】
<script>
ons.ready(function() {
var mode = "icon";
//① プッシュ通知から開かれた場合のみ実行される
window.FirebasePlugin.onNotificationOpen(function(notification){
mode = "push";
・・・・
});
//②...初期化処理initをmodeを引数にして起動
init(mode);
});// ons.ready()ここまで
</script>
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+6
なるほど、なんとなく分かりました。
確かに難問ですね。
というのも、この部分が非同期実行されることにあります。
// ① プッシュ通知から開かれた場合のみ実行される
window.FirebasePlugin.onNotificationOpen(function(notification){
mode = "push";
// ・・・・
});
JavaScriptに於けるonXXX
というメソッドは、
「もし○○の条件を満たしたら渡した関数実行してね、じゃ俺は下の行を実行するから〜」
…という作りになっています。
JavaScriptはシングルスレッドで動作するので、
ネットワークやファイルI/Oの操作を待っているとその度にプログラムがプチフリしまくる事になります。
その対策として、非同期処理は待たずにさっさと次の行を実行する設計を選択しています。
質問文との相性が悪い原因は、
1が行われるか行われないか全くわからない状況で2の処理を待つか実行するかを選択しなければならない
…という究極の二択を迫られている事にあります。
- 一生Push通知クリック判定を待ち続けて初期化処理が行われない
- Push通知クリック判定を無視してさっさと初期化処理を継続するか
これはどうしようもない問題なので、設計を変えましょう。
Push通知の目的ってそもそもなんですか?
例えばTwitterのアプリだったりした場合、
友達のリプライやリツイートのPush通知をタップします。
そのタップしたリプライやリツイートのページを開きたいのではないでしょうか?
なので、Push通知タップからのinitメソッドを実行することは諦めて、
Push通知タップはあくまでその画面へ遷移する為のトリガーにしましょう。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+4
Onsen UI はよくわかりませんが、window.FirebasePlugin
が ons
のコード内で定義されていないなら、下記のような方法で出来ると思います。
var mode = "icon";
//① プッシュ通知から開かれた場合のみ実行される
window.FirebasePlugin.onNotificationOpen(function(notification){
mode = "push";
・・・・
});
ons.ready(function() {
//②...初期化処理initをmodeを引数にして起動
init(mode);
});// ons.ready()ここまで
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+4
kei344 さんのコメント欄のアドバイスで解決可能と考えていたので、この展開は意外でした。
同期処理
ただし、①は実行されるとは限らない。
②は必ず実行されるが、①よりも後に実行され、
①で値がセットされていれば、①の値を使って②を処理したい。
同期処理でこの課題を解決するのは簡単です。
'use strict';
var foo = {
a: 0,
process1: function process1 () {
this.a = 1;
console.log('①');
},
process2: function process2 () {
console.log('②', this.a);
}
}
var bar = 1;
if (bar) {
foo.process1();
}
foo.process2();
しかしながら、ons.ready
, window.FirebasePlugin.onNotificationOpen
はイベント駆動なので、使用しているライブラリ/DOMの「イベントモデル」のルール内で対応するハンドラ関数が発火します。
イベントモデル
kei344 さんが紹介して下さったURLからリンクを辿ると、イベントの発火順に触れた記事が見つかります。
Google先生に尋ねれば、pluginが利用可能になるタイミングが deviceready イベントである事が分かるので、次のように書きます。
document.addEventListener("deviceready", function handleDeviceReady (event) {
console.log(event.type); // "deviceready"
window.FirebasePlugin.onNotificationOpen(function(notification){
console.log('notificationOpen');
});
}, false);
ons.ready(function() {
console.log('ons.ready: Onsen UI load complete');
});
このコードで「deviceready -> ons.ready」の順番で発火する事は保証されているので、「onNotificationOpen と ons.ready のどちらが先に発火するイベントモデルなのか」が問題ですが、検証すればすぐに結果は出るでしょう。
(考えづらいと思いますが、もし、ons.ready
が先に発火するのであれば、onNotificationOpen が発火するまで数秒間待機させるか、別のイベントを探す必要があります。)
NotificationOpen イベント
1. App is in foreground:
i. User receives the notification data in the JavaScript callback without any notification on the device itself (this is the normal behaviour of push notifications, it is up to you, the developer, to notify the user)
2. App is in background:
i. User receives the notification message in its device notification bar
ii. User taps the notification and the app opens
iii. User receives the notification data in the JavaScript callback
NotificationOpen イベントはバックグラウンド実行されたアプリから復帰した時にも発火するイベントです。
従って、先のコードで処理を行うと既に初期化処理が終わっているアプリに対して、バックグラウンドから復帰時にもう一度初期化処理が走る事になります。
それが望まない動作であるなら、フラグ管理する等して初期化済かどうかを判断する条件分岐処理が必要になります。
ちなみに、Monaca には resume
イベントが用意されているようです。
Re: Clavat さん
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+3
おそらくkei344さんが正しい解説をつけてくれているようなので
ロジックの話だけ
コード内の①と②に ①⇒②の処理順を設定したい。
ただし、①は実行されるとは限らない。
②は必ず実行されるが、①よりも後に実行され、
①で値がセットされていれば、①の値を使って②を処理したい。
論理的に矛盾しているように見受けられます
「②は必ず実行されるが、①よりも後に実行される」ということは
②は①に依存しているということです。
さらに「①は実行されるとは限らない」わけですから
それに付随して「②は実行されるとは限らない」状態です
その状態で「②は必ず実行される」となっているので、条件があいません。
流れとしては
- ①が実行されたら②は実行される
は確定だとして
- ①が実行されなかったら②は実行されない
- ①が実行されない場合でも②は実行されるが他のなにかトリガーが定義されている
のどちらかを定義する必要があります
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
皆さんご回答ありがとうございます。
お三方みなさんから、何がダメなのかをご教授いただきました。
皆さんにベストアンサーを付けたいところですが、1名だけということですので、
こちらの知識レベルとやりたいことに寄り添っていただき、
わかりやすいお答えをいただいたmiyabi-sanとさせていただきました。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
プラグインを知らない、回答を全て読んでいませんが…
promiseを使って、非同期処理にすればよいのではないでしょうか?
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.32%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2017/11/13 15:27 編集
>これはどうしようもない問題なので、設計を変えましょう。
この部分までのご説明、まさにその通りですごく納得です。
第三者のプラグインなので onNotificationOpen が何をしているか詳細が
ブラックボックスではあるのですが、おそらく onXXX だと思います。
--------
プッシュ通知の目的は、お察しの通り、プッシュ通知に含まれるURLを開くことです。
現状のinit処理では、アプリのトップ画面(のURL)を表示するようにまとめてしまっています。
実現したいことは、
プッシュ通知でアプリを開いた場合、プッシュ通知で指定された画面を開く
プッシュ通知ではなくアイコンでアプリを開いた場合、トップ画面を開く
というものです。
これまでの設計(思想)では、
プッシュ通知で開いたかどうかを初期処理よりも先に判断したい。
プッシュ通知だったらそのURLを保持した状態で、初期処理をしたい!
でした。
そうではなく、初期処理は最初に行って終わらせておく。
(プッシュ通知に依存するな、待つな が大原則)
↓
その上でもしプッシュ通知が開かれたのであれば、
開かれたときの処理(表示画面を変える)を実行する(後から実行して、画面を書き換えるとか)
というような作りに変更したほう良さそうってことですね。
2017/11/13 15:37 編集
ああでもでも…他の既製品、例えばTwitterクライアントとかの仕様ですが、
Push通知をタップした時の挙動は、
URLみたいなパラメータを付属してアプリを起動してるんじゃないですか?
質問文開始時点が前提条件、つまり起動後のJS面からのアプローチのみではお手上げですが、
最初のタップ起動か普通にアプリ起動かを、
「付帯パラメータの有無等を使って同期判別出来る」のであれば話は変わってきますね。
私はCordovaの事は詳しくないのですが、
パラメータ付与起動が出来ないか、それを同期で判別出来ないかもう一度調査してみてはどうでしょうか?
2017/11/13 15:49
(認識が間違っている可能性多々ありますが、その辺りは調べておりまして)
ネイティブアプリであれば、アプリを開く時点で
プッシュ通知のメッセージ(オブジェクトみたいなもの)そのものがあるかを
判断する方法はあるようです。
ただ私が使用している Cordova + プラグイン という環境では、
それに該当しそうなものを見つけられませんでした。
ですので、プッシュ通知をタップして起動する過程においては、
①アプリが起動する(ons.readyに書いた初期処理が実行される)
↓
②プッシュ通知を開く(プッシュ通知メッセージ内にURL情報を埋め込んでいるので、それを見る)
という方法を取るしかなさそうです。
最初したかったことは「先にプッシュ通知のURLを取得した状態」で「初期処理の続きをしたい」
でした。
既に組んでいる関数の順序や分割・統合など、処理の組み替え等に取り込んでみようと思います!
2017/11/13 16:13
その解決策として捻り出したのが本質問だったわけですね。
承知しました、残りの処理頑張ってください。