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

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

ただいまの
回答率

90.03%

$.when().の後のdone(function(){})の処理をしてくれない

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 85

pegy

score 97

Leaflet.jsというMap操作関連のライブラリを使いながら非同期で現在地を取得してユーザーのviewを移動する(flyTo method)コードを以下の通り記載しているのですが、done()部分がwhenの後に実行されません。
もし原因がわかる方がいらっしゃればアドバイスをいただけると幸いです。
よろしくお願いいたします。

<style>.errDisplay{display:none;}</style>
<div class="errorBox errDisplay">error</div>
var $defer = new $.Deferred();
var opts ={
  enableHighAccuracy: true,
  timeout: 5000,
  maximumAge: 0
}

/*move current*/
$('.current').on('click',function(){
  $.when(
    $defer,
    $('.errorBox').removeClass('errDisplay'),
    navigator.geolocation.getCurrentPosition(cpSuccess,cpFail,opts)
  ).done(function(){
    setTimeout(function(){
     alert("here");//ここが処理されません
     return $defer.resolve();
  },1000)
     return $defer.promise();
  })
})

/*current position*/
function cpSuccess(position){
  const currentLat = position.coords.latitude;
  const currentLng = position.coords.longitude;

  map.flyTo([currentLat,currentLng],14);

  if (current_marker) {
    map.removeLayer(current_marker);
  }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

ざっと見ただけですし、実際に走らせてもいないので間違っているかもしれませんが。

$.when( の次の行の $defer, が原因ではないでしょうか。
これがあるために $.when が終了せず、done が実行されないのだと思います。
この行の意図を確認し、不要なら消してみてください。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/11/09 16:57

    コメントありがとうございます、失礼いたしました。
    ●実現したいこと
    1.currntをクリックする
    2..errorBoxからerrDisplayが.removeClassされる
    3.whenのなかでnavigator.geolocation.getCurrentPosition(cpSuccess,cpFail,opts)関数が実行される、具体的にはgeolocationで位置情報が取得されてleafletライブラリのflyToメソっドで現在の視点が現在地に移る
    4.3.の処理が終わるとerrorBoxにerrDisplayがaddClassされる
    もっとも重要なことは3の処理が終わった後に4が実行されてほしいということになります。
    ●お二方のご回答から修正したコード
    $('.current').on('click',function(){
    $('.errorBox').removeClass('errDisplay')
    if (navigator.geolocation) {
    $('.loading').removeClass('loading_hide')
    $.when(
    $defer
    ).done(function(){
    $('.errorBox').addClass('errDisplay')
    })
    setTimeout(function(){
    navigator.geolocation.getCurrentPosition(cpSuccess,cpFail,opts)
    $defer.resolve();
    },1000)
    }else{
    alert("位置情報の取得に失敗しましたerr:[1]")
    }
    })
    ●発生している問題
    もともとの問題であったremoveClassとaddClassが同時に処理されてしまう点は解消したのですが、このコードによってremoveClassされてから1秒後にはaddClassが実行されてしまう、つまり上記でいう3の処理はまだ終わっていないにもかかわらず4が実行されているということが現在の問題です。3の処理はgeolocationを利用しているので現在地を取得する時間は1秒のこともあれば5秒のこともあり予測することができないため、3の処理が終わればという条件で4を実行したいのですがそれが実装できないという点にございます。

    何かあればまたご指摘をいただければ幸いです。
    よろしくお願い申し上げます。

    キャンセル

  • 2019/11/09 18:27

    根本的な問題として、非同期処理が理解できていないようで、やりたいことと実際のコードが大幅に乖離しているようです。

    ブレークポイントを設定したり、コードに console.log を追加するなどで、コードがどういう順番で実行されているか確認してみてください。
    3の処理が終わった後に4が実行されていないとかそういうレベルではなく、メチャクチャな順番で実行されてしまっていることがわかると思います。
    これを修正することは回答の範疇を超えます。

    非同期処理(navigator.geolocation.getCurrentPosition ではコールバックが使われているので、必要ならコールバックも)をきちんと理解し、その上で自分で修正してみて、それでもわからなければそのときに質問し直してください。

    キャンセル

  • 2019/11/09 21:42

    コメントありがとうございます。承知をいたしました、地道に確認をしてみようと思います。
    よろしくお願い申し上げます、

    キャンセル

+1

括弧の対応がとれていないため、構文エラーでそもそも実行されていないと思います。コンソールにエラーが出力されていませんか? それともコードの後半を掲載しなかっただけでしょうか。

また、$when の引数に渡されている $defer が、$defer が解決されるまで解決されないようになっているため、 .done() に渡されたコールバックが実行されることはありません。

ご提示のコードは色々とおかしなコードになってしまっていますので、まずは非同期処理の基本から順を追って理解していくといいと思います。

var $defer = new jQuery.Deferred()
$.when($defer).done(function() {
  //下に記載されている $defer.resolve() が実行された後に実行される。
  alert('その後に実行')
})
setTimeout(function() {
  // 1秒後にここが実行される
  alert('1秒経過')
  $defer.resolve()
}, 1000)

このように、 when() に渡したオブジェクトがすべて解決されると、 when に渡したコールバック関数が実行されます。 when() に渡すコールバック関数の中に遅延されたオブジェクトの解決処理を入れてしまうと、その処理は永久に実行されません。

逆に、 Deferred や Promise のような、将来解決されることを意図していないオブジェクトを when の引数に与える意味はありません。($('.errorBox').removeClass('errDisplay') など)

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/11/09 01:17 編集

    コメントいただきありがとうございます。ざっくりと順番を考えた場合
    1) whenの引数の$deferが解決される
    2) doneに渡したコールバック関数が実行される

    したがって、私が記載したコードはdone()の中に$defer.resolve()としてしまっているため、whenが解決されることがなくdoneのコールバック関数が実行されることがないと理解することができました。また意図した動作を実現することができました。

    一方、以下の2点だけ理解できない点があり、これを今調べているのですが、一旦コメントバックさせていただきます。
    ・$defer.resolve()としてdeferオブジェクトのresolveメソッドで解決するのとreturn $defer.resolve()でオブジェクトを返してもどちらでも解決して処理がdoneに移るのですが
    ・promiseについて混乱してきました。。。deferオブジェクトにpromiseメドッドがあったりpromiseオブジェクトにresolveメソッドがあったりそれぞれの役割がどうしても抽象的でわからないので、これは以下のような記事を見ながら奮闘中です、、、難しい
    https://app.codegrid.net/entry/deferred-1

    気づきをたくさん与えてくださり、本当にありがとうございます

    キャンセル

  • 2019/11/09 01:38

    いえ、申し訳ございません、私の勘違いが一つございました。解決したと思ったのですが、結果的に1秒待ってdoneの処理がされているだけで本来意図している、whenのなかの処理、つまり
    「現在位置を取得してflyToメソッドで視点を移し終えたら」次に「removeClassする」ではなく
    「1秒経過したら」次に「removeClassする」となっておりました。

    キャンセル

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

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