質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.98%

【JavaScript】処理に前後関係を付けるロジックを組みたい(お知恵をお借りしたい)

解決済

回答 6

投稿 編集

  • 評価
  • クリップ 3
  • VIEW 1,885

Clavat

score 84

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ページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 6

checkベストアンサー

+6

なるほど、なんとなく分かりました。
確かに難問ですね。
というのも、この部分が非同期実行されることにあります。

// ① プッシュ通知から開かれた場合のみ実行される
window.FirebasePlugin.onNotificationOpen(function(notification){
  mode = "push";
  // ・・・・
});

JavaScriptに於けるonXXXというメソッドは、
「もし○○の条件を満たしたら渡した関数実行してね、じゃ俺は下の行を実行するから〜」
…という作りになっています。

JavaScriptはシングルスレッドで動作するので、
ネットワークやファイルI/Oの操作を待っているとその度にプログラムがプチフリしまくる事になります。
その対策として、非同期処理は待たずにさっさと次の行を実行する設計を選択しています。


質問文との相性が悪い原因は、
1が行われるか行われないか全くわからない状況で2の処理を待つか実行するかを選択しなければならない
…という究極の二択を迫られている事にあります。

  • 一生Push通知クリック判定を待ち続けて初期化処理が行われない
  • Push通知クリック判定を無視してさっさと初期化処理を継続するか

これはどうしようもない問題なので、設計を変えましょう。


Push通知の目的ってそもそもなんですか?

例えばTwitterのアプリだったりした場合、
友達のリプライやリツイートのPush通知をタップします。
そのタップしたリプライやリツイートのページを開きたいのではないでしょうか?

なので、Push通知タップからのinitメソッドを実行することは諦めて、
Push通知タップはあくまでその画面へ遷移する為のトリガーにしましょう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 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

    そこは予め調査済みだったのですね。

    その解決策として捻り出したのが本質問だったわけですね。
    承知しました、残りの処理頑張ってください。

    キャンセル

+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()ここまで

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/13 14:02

    >ons.ready の外側で console.log(window);でFirebasePluginがそこに入っているか。

    入っていないようです・・・。
    入っていないから ??? の状態で
    せっかくのご回答の意図・原因を理解できていなくて申し訳ありません。

    別の回答にもレスしましたが、
    ①が実行されるときでも、現状、
    var mode = "icon"; ⇒  ②init(mode); ⇒ ①mode = "push";
    という順番で稼働してしまうので、なんとか①⇒②に出来ないか、という意図です。

    現状②⇒①の順になってしまうので、②の処理を後から①で書き換えるしか方法がないのかな、
    というところでの悩み(良いロジックがないのか)です。

    もう少し考えてみます。アドバイスありがとうございます。

    キャンセル

  • 2017/11/13 14:14

    じゃあプラグイン読み込み後のイベントにフックすればよい。

    【[Monaca] CordovaプラグインとOnsen UIの利用について - Qiita】
    https://qiita.com/keeey/items/310811eb4e879a8a38f8

    キャンセル

  • 2017/11/13 14:56

    回答ありがとうございます。
    フックの方法(やりかた)を含めて考えてみます。

    キャンセル

+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.readywindow.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 さん

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/13 18:37

    ご回答ありがとうございます。

    Monaca関連で質問していたのですが、回答が付かなかったため、
    出来るだけ多くの方からアドバイスが頂けたらと思い、
    MonacaやCordovaの説明部分(共有したいこと)をそぎ落とし、
    JavaScriptの部分だけにして質問しなおしたのですが、
    それが逆に話をややこしくしてしまいました・・・。
    申し訳ございません。


    まず質問記載時点で kei344さん、think49さん が教えてくださった
    イベント発火順といいますか、pluginが利用可能になるタイミングが
    Cordovaのdeviceready イベントであること、
    ons.ready がCordovaの準備完了も内包する(表現が正しくないかもです)ことは
    理解していたつもりです。

    個人的に理解が弱かった部分として(現時点の認識として)は、
    onNotificationOpen が非同期処理というところで
    (「もし○○の条件を満たしたら渡した関数実行してね、じゃ俺は下の行を実行するから〜」)
    miyabi-sunさん、yambejpさんが回答してくださった通り、
    実行されるかわからないものを先行にして、
    必ず実行されるものを後続に付けようとしていたのが、そもそも間違いだから、
    ロジックの組み立てが間違いなんだな、という理解です。

    私自身、あまりJavaScriptに精通していないこともありますが、
    現時点で「論理矛盾」だと思っています。

    もし「こう書いたらすぐに解決出来るだろうに・・・」という意味でご回答いただいていましたら、
    私の中で皆様の頭の中にあるコードが浮かんでいないのだと思います。

    そういう意味で、think49さんがご指摘の
    ①kei344 さんのコメント欄のアドバイスで解決可能
    ②この展開
    が何を指されているか、すみませんが理解が及んでいません。

    こう書いたらいいのでは? というサンプルコードがあれば「なるほど!」と
    理解できるような気がしていますが、お恥ずかしいですが、
    ① ⇒ どう書けば解決するの?
    ② ⇒ 考え直さなくてもいいの?
    という状況ですので、

    初期処理は最初に行って終わらせておく。
    (プッシュ通知に依存するな、待つな が大原則)

    その上でもしプッシュ通知が開かれたのであれば、
    開かれたときの処理(表示画面を変える)を実行する(後から実行して、画面を書き換えるとか)
    というような作りに変更

    しようとコードの見直しを進めている状況です。

    キャンセル

  • 2017/11/13 18:42

    追伸

    NotificationOpen イベントはバックグラウンド実行されたアプリから復帰した時にも発火するイベントです。従って、先のコードで処理を行うと既に初期化処理が終わっているアプリに対して、バックグラウンドから復帰時にもう一度初期化処理が走る事になります。

    ⇒はい、プッシュ通知起動による復帰でも「初期化処理が走る」で、今のところ問題ありません。

    キャンセル

+3

おそらくkei344さんが正しい解説をつけてくれているようなので
ロジックの話だけ

コード内の①と②に ①⇒②の処理順を設定したい。 
ただし、①は実行されるとは限らない。
②は必ず実行されるが、①よりも後に実行され、
①で値がセットされていれば、①の値を使って②を処理したい。 

論理的に矛盾しているように見受けられます

「②は必ず実行されるが、①よりも後に実行される」ということは
②は①に依存しているということです。

さらに「①は実行されるとは限らない」わけですから
それに付随して「②は実行されるとは限らない」状態です

その状態で「②は必ず実行される」となっているので、条件があいません。

流れとしては

  • ①が実行されたら②は実行される

は確定だとして

  • ①が実行されなかったら②は実行されない
  • ①が実行されない場合でも②は実行されるが他のなにかトリガーが定義されている

のどちらかを定義する必要があります

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/13 13:54

    説明が悪く申し訳ありません。

    ①が実行されないときは
    var mode = "icon"; ⇒ ②init(mode); でOKです。

    ①が実行されるときでも、現状、
    var mode = "icon"; ⇒  ②init(mode); ⇒ ①mode = "push";
    という順番で稼働してしまうので、なんとか①⇒②に出来ないか、という意図です。

    キャンセル

  • 2017/11/13 14:03

    まさに前回書いた、どちらかの定義をしていないからでは?
    ①が実行されるのを待たないで②が実行されるなら、
    別のトリガー③あるということですよね?
    その際①→②を保証したいのであれば
    ③を発行するときに①が実行されないことをチェックする仕組みを組むことです
    そうでないかぎり②は①を待ち続けるか、①を無視するかのどちらかでしょう

    キャンセル

  • 2017/11/13 14:20

    そうですね。ロジックとしては理解できました。

    Cordovaという開発環境でいうと、
    Cordovaが準備できたら(≒アプリが起動したら)実行する処理を
    ons.ready(function() {}); に記載しないといけないので、
    ②init(mode); の実行トリガー③はそもそも ons.ready(function() {}); になる理解です。

    そう考えると①の実行トリガー③も ons.ready(function() {}); ということになりそうですが、
    ①⇒②で動いてくれていないので
     ※ブラックボックスである関数 window.FirebasePlugin.onNotificationOpen が
      プッシュ通知メッセージのオブジェクト notification があるとき
      (プッシュ通知を開いたときに対して)のみ動いていて、
      notification がないときは実行されていない感じです。
    これには別のトリガー④がある(③と④の両方がトリガー?)ということになってしまいますよね。
    (中に何を書いてもプッシュ通知を開いたときでないと実行されていない状況です)

    ありがとうございます、もう少し処理の起動条件がうまく書けないか、
    別のフラグや、処理を組み替える等、試してみます。

    キャンセル

0

皆さんご回答ありがとうございます。
お三方みなさんから、何がダメなのかをご教授いただきました。
皆さんにベストアンサーを付けたいところですが、1名だけということですので、
こちらの知識レベルとやりたいことに寄り添っていただき、
わかりやすいお答えをいただいたmiyabi-sanとさせていただきました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

プラグインを知らない、回答を全て読んでいませんが…

promiseを使って、非同期処理にすればよいのではないでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/14 09:30

    ご回答ありがとうございます。

    事前に試しておりましたが、
    window.FirebasePlugin.onNotificationOpen が「プッシュ通知を開いたとき」に
    実行されるもので、「アイコンから起動したとき」は実行されないので、
    initの先行条件として定義できない  と理解しております。

    キャンセル

  • 2017/11/14 09:55

    理解できました。
    もしかしたらと思い、よく読まずにコメントしてしまいました。
    私も勉強になりました。ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.98%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる