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

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

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Ajax

Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

Q&A

3回答

5775閲覧

jQuery の Deferred や when / then による同期的な処理の後の `return` を返す方法

yasshcy

総合スコア6

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Ajax

Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

0グッド

0クリップ

投稿2018/08/21 07:06

前提・実現したいこと

jQuery で以下のような
API【A】へリクエスト、そのレスポンス値を使って
API【B】へリクエスト、そのレスポンス値を使って
オブジェクトに格納し、 return でデータを渡せるようなものを書いています。

jQuery の Deferred や when / then による同期的な記述をしていますが
以下の記述だと、最終的な return が同期的に返せておらず、
どの記述が良いか、ご教授、アドバイスをいただけると嬉しいです。

発生している問題・エラーメッセージ

該当のソースコードの ※ の部分が同期的に return が取れず困っている箇所です。

該当のソースコード

javascript

1$(function() { 2 3 'use strict'; 4 5 function parent () { 6 7 // 最終的に `parent` を外のjsから実行した時の return に利用したい 8 var result = []; 9 10 // childBの処理で利用したい配列。中身はchildAで作成する 11 var useB = []; 12 13 // APIのURL 14 var jsonA = 'https://www.hoge.jp/apiA.php'; 15 var jsonB = 'https://www.hoge.jp/apiB.php'; 16 17 function childA () { 18 19 // 同期を取るため延期させるDeferredオブジェクト作成 20 var d = new $.Deferred(); 21 22 // Ajax 23 $.ajax({ 24 url: jsonpA, 25 dataType: 'jsonp', 26 jsonpCallback: 'jsonpA', 27 cache : true, 28 }) 29 .then( function( res ) { 30 31 // 取ってきたマニュアル jsonp を eachで回して 32 $( res ).each( function (index, el) { 33 34 // childBで利用するためのデータを格納 35 useB.push({ el.hoge }); 36 37 }); 38 39 useB = useB.join(); 40 41 // Deferred 延期の解決 42 d.resolve(); 43 44 }); 45 46 // プロミス作ってDeferredを返す 47 return d.promise(); 48 49 } 50 51 function childB () { 52 53 // 同期を取るため延期させるDeferredオブジェクト作成 54 var d = new $.Deferred(); 55 // Ajax 56 $.ajax({ 57 url : jsonB, 58 dataType : 'jsonp', 59 jsonpCallback : 'jsonB', 60 cache : true, 61 }) 62 .then( function ( res ) { 63 64 // ここで 最終的に return で利用したいデータを蓄積する 65 $( useB ).each( function (index, el) { 66 67 // 足していく 68 result.push({ 69 'test01' : el.element01, 70 'test02' : el.element02 71 }); 72 73 }); 74 75 // Deferred 延期の解決 76 d.resolve(); 77 78 }); 79 80 // プロミス作ってDeferredを返す 81 return d.promise(); 82 83 } 84 85 // childAを実行してchildBを実行 86 $.when( 87 88 childA() 89 90 ) 91 .then(childB) 92 93 // ※ ここに書いても結局 when then を待たず時実行される。。同期的ではない 94 return result; 95 96 } 97 98 // windowオブジェクトに追加して外部jsから呼び出せるようにする 99 window.parent = parent(); 100 101});

これを読み込んだ後に別のjs(またはhtml上で)で parent として呼び出して利用しています。

試したこと

上記だと、 $when / then から外れた所で return を書いているので同期的にならないと考え以下のように記述したのですが、
その場合、 function parentreturn としては扱われないので、困りました。

javascript

1$(function() { 2 3 'use strict'; 4 5 function parent () { 6 7 // 最終的に `parent` を外のjsから実行した時の return に利用したい 8 var result = []; 9 10 // childBの処理で利用したい配列。中身はchildAで作成する 11 var useB = []; 12 13 // APIのURL 14 var jsonA = 'https://www.hoge.jp/apiA.php'; 15 var jsonB = 'https://www.hoge.jp/apiB.php'; 16 17 function childA () { 18 19 // 同期を取るため延期させるDeferredオブジェクト作成 20 var d = new $.Deferred(); 21 22 // Ajax 23 $.ajax({ 24 url: jsonpA, 25 dataType: 'jsonp', 26 jsonpCallback: 'jsonpA', 27 cache : true, 28 }) 29 .then( function( res ) { 30 31 // 取ってきたマニュアル jsonp を eachで回して 32 $( res ).each( function (index, el) { 33 34 // childBで利用するためのデータを格納 35 useB.push({ el.hoge }); 36 37 }); 38 39 useB = useB.join(); 40 41 // Deferred 延期の解決 42 d.resolve(); 43 44 }); 45 46 // プロミス作ってDeferredを返す 47 return d.promise(); 48 49 } 50 51 function childB () { 52 53 // 同期を取るため延期させるDeferredオブジェクト作成 54 var d = new $.Deferred(); 55 // Ajax 56 $.ajax({ 57 url : jsonB, 58 dataType : 'jsonp', 59 jsonpCallback : 'jsonB', 60 cache : true, 61 }) 62 .then( function ( res ) { 63 64 // ここで 最終的に return で利用したいデータを蓄積する 65 $( useB ).each( function (index, el) { 66 67 // 足していく 68 result.push({ 69 'test01' : el.element01, 70 'test02' : el.element02 71 }); 72 73 }); 74 75 // Deferred 延期の解決 76 d.resolve(); 77 78 }); 79 80 // プロミス作ってDeferredを返す 81 return d.promise(); 82 83 } 84 85 // childAを実行してchildBを実行 86 $.when( 87 88 childA() 89 90 ) 91 .then(childB) 92 .then(function(){ 93 // ※ ここで return させても `parent` の return としては扱われない。。 94 return result; 95 }) 96 97 } 98 99 // windowオブジェクトに追加して外部jsから呼び出せるようにする 100 window.parent = parent(); 101 102});

どうぞよろしくお願いします。

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

同期を取りたいparentでpromiseの終了を待つ必要があると思います。

childBのpromiseをreturnして、parent側で、戻されたpromiseに対して.thenする感じで、処理できませんか?

追記

若干怪しいところがあるかもしれませんが・・・
概ねこんなイメージでしょうか?

(説明に不要と思った部分は、オリジナルソースから省略しました)

function parent () { // parentのDeferredオブジェクト作成 var parent_dfd = new $.Deferred(); // 最終的に `parent` を外のjsから実行した時の return に利用したい var result = []; function childA () { var d = new $.Deferred(); // Ajax $.ajax({ url: jsonpA, }) .then( function( res ) { ... d.resolve(); }); return d.promise(); } function childB () { // Ajax $.ajax({ url: jsonpB, }) .then( function ( res ) { $( useB ).each( function (index, el) { result.push({ 'test01' : el.element01, 'test02' : el.element02 }); }); // parentのDeferredを解決 parent_dfd.resolve(result); }); } childA() .then(childB); return parent_dfd.promise(); } // windowオブジェクトに追加して外部jsから呼び出せるようにする window.parent = parent(); });

以下、呼び出し側

parent().then(function (result) { //resultの処理 });

投稿2018/08/21 07:17

編集2018/08/21 09:12
Meganezaru

総合スコア715

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yasshcy

2018/08/21 08:18 編集

ありがとうございます。 > 同期を取りたいparentでpromiseの終了を待つ必要があると思います。 確かに、今parentの処理では Deferred の作成と prromise は持っていないです。 > childBのpromiseをreturnして、 今、childB で `return d.promise();` となっている所で `promise` せず `return` をして > parent側で、戻されたpromiseに対して.thenする感じ、処理できませんか? こういう感じでしょうか。 ``` // childAを実行してchildBを実行 $.when( childA() ) .then( childB, function () { return d.promise(); } ) ``` うまくは行きませんでしたが、 > parent側で、戻されたpromiseに対して.thenする感じ がイマイチピンときませんでした。。すみません。
Meganezaru

2018/08/21 08:53

すいません、parentの定義をあやふやにしてました。 私がparentと示したのは、parentの呼び出し側のことでしたが、 よくよく考えたら、私の知識では、childBのpromiseをparentのreturnとして返す方法が、見つけられませんでした(>_<) おっしゃる通り、parentにdeferredを作成して、promiseをreturnし、childBの中で、そのdeferredをresolveするイメージになると思います。その場合、childBのdeferredは不要だと思います。 のちほど、回答に追記します。
guest

0

jsonpなのですからコールバックのjsonpAからjsonpBを呼ぶだけでは?

sample

javascript

1<script> 2$(function(){ 3 $('#btn').on('click',function(){ 4 $.ajax({ 5 url: 'ajax.php', 6 data:{"id":1}, 7 dataType: 'jsonp', 8 jsonpCallback: 'jsonpA', 9 cache : true, 10 }); 11 }); 12}); 13function jsonpA(data){ 14 data.id=data.id+"23"; 15 $.ajax({ 16 url: 'ajax.php', 17 data:data, 18 dataType: 'jsonp', 19 jsonpCallback: 'jsonpB', 20 cache : true, 21 }); 22} 23function jsonpB(data){ 24 console.log(data); 25} 26</script> 27<input type="button" id="btn" value="test">
  • ajax.php

PHP

1<?PHP 2$callback=filter_input(INPUT_GET,"callback"); 3if(is_null($callback)) $callback="void"; 4$id=filter_input(INPUT_GET,"id"); 5$data=["id"=>$id]; 6header("Content-Type:application/javascript"); 7print "{$callback}(".json_encode($data).");"; 8?>

投稿2018/08/21 08:13

yambejp

総合スコア114767

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yasshcy

2018/08/21 10:03

ありがとうございます。 このcallbackを自由に決めてよければ、この方法がsimpleかもしれませんね。 ただ、今回webAPIの仕様上、jsonp取得のcallbackは指定されていているので 今回のケースでは適用できなさそうです。
yambejp

2018/08/22 04:11

> callbackを自由に決めてよければ ごめんなさい、意味がわかりません ご自身が例示したsample自体にコールバック関数名を指定していますよね? jsonpなのに指定したコールバック名でデータを返してくれないのでしょうか?
yasshcy

2018/09/03 03:35

説明が足らず申し訳ありません。 そしてレスが遅くて申しわけありません。。 > ご自身が例示したsample自体にコールバック関数名を指定していますよね? はい、sampleに `jsonpCallback: 'jsonpA',` とコールバック関数を指定しています。 > jsonpなのに指定したコールバック名でデータを返してくれないのでしょうか? webAPIは別途、他の人が用意したものなのですが、これに問い合わせると以下のような形でレスポンスが返ってきます。 ``` jsonpA({ "list": [ { "property1": "value1", "property2": "value2" } ] }) ``` 上記の `jsonpA` は js側で指定している `jsonpCallback: 'jsonpA',` の値と一致していなければならないと、思っています。 これはwebAPI(php)側で指定されているので、変更できない、という意味で申し上げました。
yambejp

2018/09/03 03:45

だから、jsonpでjsonpAの引数でjsonデータが返るわけですから 私が最初に提示したとおりの処理でよいのでは? jsonpAはユーザー関数を用意してくださいという意味ですから
guest

0

コールバックでよかったら引数つけて呼べばいいのでは?未検証ですが

javascript

1function parent (cb) {

javascript

1 .then(childB) 2 .then(function(){ 3 // ※ ここで return させても `parent` の return としては扱われない。。 4 //return result; 5 cb(result); 6 })

呼び出し

javascript

1 window.parent(function(result){ 2 // result使って処理 3 })

投稿2018/08/21 08:02

sousuke

総合スコア3828

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yasshcy

2018/08/21 09:56 編集

ありがとうございます。 呼び出しの引数を `result` => `cb` に変更した所、 # 呼び出し ``` window.parent(function(cb){ // result使って処理 }) ``` ちゃんと処理を待ってくれました。 callbackでの順番待ちはうまく行きました。ありがとうございます。 ただ、これはもともとの私の構造が悪いのかもしれませんが、 今回全体を ``` $(function() { function parent () { 〜 略 〜 } }); ``` で囲ったものを、外から実行できるようにする必要があったので、 外から実行した際に `undefined` になりオブジェクトが受け取れませんでした。 もしかしたら、即時関数で囲うこと自体がナンセンスかもしれませんね。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問