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

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

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

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

jQuery

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

Q&A

解決済

2回答

1607閲覧

JSで外部APIが値を取得するまで、次の処理に行くのを保留する方法

huyumin

総合スコア16

JavaScript

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

jQuery

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

0グッド

2クリップ

投稿2019/08/18 14:04

編集2019/08/18 23:00

###実現したいこと
メールアドレスの入力フォーム実装に取り組んでいて、値の検証方法として下記3つを考えています。これらを実現したいです。

➀捨てアドを検証
➁既存を検証
➂文字を検証

###発生している問題
上の➀は外部APIによる検証なのですが、検証結果(truefalse)の取得に時間がかかってしまいます。
なので「trueならクラスを外す」などの条件分岐ができません。

後述のように$.when$.Deferredも使いどころがわからず苦戦しています。
もし正しい使い方や他の解決策があればご指導いただきたいと思います。

###該当のソースコード
現状の流れは下記です。
➀➁➂をまとめて検証するvalidMailTotal()という関数を用意し、それがtrueを返せば#mailBoxから.dameを外す。という処理にしています。

<input type="text" id="mailBox" class="dame"><br> <script> // メールアドレスの入力 $( document ).on( 'input', '#mailBox', function() { const inputVal = $( this ).val(); // 入力値を取得 const allow = validMailTotal( inputVal ); // 入力値がOKならtrue console.log( allow ); if( allow ){ $( this ).removeClass('dame'); } else{ $( this ).addClass('dame'); } });

下記がまとめて検証する関数validMailTotal()で、➀➁➂が終わってから検証結果を返すように$.whenを使ってみました。しかしこれがundefinedを返すのです。

// メールアドレスを検証 function validMailTotal( inputVal ){ let sute, exist, match; $.when( // ➀捨てアドを検証 sute = validMailSute( inputVal ), // ➁既存を検証 exist = validMailExist( inputVal ), // ➂文字を検証 match = validMailMatch( inputVal ) ) .done(function(){ // 全ての検証がOKならtrueを返す if( sute && exist && match ){ return true; } }); }

そして➀➁➂の検証がそれぞれ下記で、検証結果がOKならtrueを返すというものです。(➁は実際にはAjaxでDBを探しますが、順番を測る目的だけなのでsetTimeoutにしています。)

// ➀捨てアドを検証 function validMailSute( inputVal ){ const xmlHttp = new XMLHttpRequest(); xmlHttp.open( "GET", "https://www.validator.pizza/email/"+inputVal+"", false ); xmlHttp.send(); const pizza = JSON.parse(xmlHttp.responseText); const disposable = pizza.disposable; // ダメなときtrueが返ってややこしいので次で逆にしとく const sute = disposable==true ? false : true; return sute; } // ➁既存を検証 function validMailExist( inputVal ){ setTimeout(function(){ // 実際にはAjaxでDBを探します const exist = true; return exist; },2000); } // ➂文字を検証 funtion validMailMatch( inputVal ){ const match = inputVal.match(/^(([^<>()[]\.,;:\s@"]+(.[^<>()[]\.,;:\s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z\-0-9]+.)+[a-zA-Z]{2,}))$/); return match; }

###ためしたこと
先述のようにまとめて検証する関数validMailTotal()undefinedを返すので他に方法はないかと思い、$.whenでなく$.Deferredなどを試してみました。しかし外部APIについて$.Deferredを書く場所がわかりません。

どうすれば➀➁➂の検証を無事にできるでしょうか。

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

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

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

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

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

huyumin

2019/08/18 17:26

そちらの質問で別のご回答が得られれば今回の質問にも試せると考えているので、解決は先延ばしにさせて頂いてます。ご不快だったでしょうか?
kei344

2019/08/18 17:32

不快というよりも、そちらの質問内容が解決していない状態で出来る質問ではないので。Deferred自体の理解がない状態でwhenを使って書くことは無謀です。
gentaro

2019/08/18 23:52

すごくどうでもいいけど、普通は③を最初にやるような気が。 (③が通らないなら①も②も通らない) そして③は非同期でやる必要がない。
azuapricot

2019/08/18 23:57

前の質問が解決してないなら、前の質問を修正するなりなんなりして解決させるべきです。 前の質問がわかってないからこの質問が出てきてるんですよね?内容は全く同じですし。
huyumin

2019/08/19 00:52

gentaro様 なるほど!ありがとうございます。
huyumin

2019/08/19 00:53

azuapricot様 前の質問はわかったけど、今回のケース(外部API)にアレンジできず質問しています。そんな初心者なりの進歩について、「内容が全く同じだ」や「べきだ」とご指摘頂きましても… ご意向に沿えずすみませんとしか m(*- -*)m
gentaro

2019/08/19 01:01

うーん、「前の質問はわかった」の意味がよくわかんないですね。 そこだけ読むと解決済みにできそうな気がするけど。 応用が効かなくて困ってる、という意味ならまぁそういう事もあるか、とは思うけど。
azuapricot

2019/08/19 01:04

「前の質問はわかったけど」 わかってないから未解決なんじゃないですか? 質問を変えたところで回答しに来ている人たちは同じですし、似たような回答しか得られません。 それなら一つの質問を最後まで逐一追記するなり、回答者にコメントで疑問点を直接聞くなどして理解に務めるほうが良いのでは?
huyumin

2019/08/19 01:15

gentaro様 前の質問についてだけ解決するコードがわかったということです。そのアレンジまでわかったというわけではないので今回の質問に至りました。
huyumin

2019/08/19 01:16

azuapricot様 >わかってないから未解決なんじゃないですか? いいえ。未解決の理由はすでに上で述べました。 >それなら一つの質問を最後まで逐一追記するなり 質問の編集が良いか新しい質問が良いかについては、これまたご意向に沿えずすみませんとしか言えません。 過分に主観的な物言いにすこし疲れたのでazuapricot様はこの辺でご容赦ください。
m.ts10806

2019/08/19 04:00 編集

第三者的な目線からは主観的な物言いとは感じませんでしたが(指摘を受けている人はそうなるかもね、くらい)、 kei344さんの「Deferred自体の理解がない状態でwhenを使って書くことは無謀です」がほぼ全てだと思います。
huyumin

2019/08/21 14:23

そんなこと言われても…
m.ts10806

2019/08/21 14:29

急がば回れです。「もうちょっとでできそう」を続けていると時間ばかりを浪費します(極端な例がパチスロです)。 ここはひとつ基本に立ち返って、本当に最小構成の簡単なコードで試すことからやってみてはどうでしょうか。最初にHello Worldをやったように、本当に最小構成のところから。
huyumin

2019/08/21 14:38

>「もうちょっとでできそう」を続けていると時間ばかりを浪費します ほんっとこれ!!プログラミングの格言ですわねこれ。
thyda.eiqau

2019/08/21 14:57

kei334さんもgentaroさんもazuapricotさんもm.ts10806さんも、共通しておっしゃっているのは、「前提がわかってないならやるな」ではなくて、「前提がわかっていないとこれをやるのはつらいから、前提をもうちょっと頑張ってみたほうが為になるよ」ということですよ。特に非同期処理はJavaScriptの肝と言ってもいいめちゃくちゃ大切な処理ですので、がんばりましょう。 プログラミングに限らずですが、長くやっているとショートカットしようとしてすごいしんどい思いをするときは必ず訪れるし、みなさんご自身の苦い経験をもとに進言されているのだと思います
guest

回答2

0

jQuery.when( deferreds )

deferreds Type: Deferred or Promise or Thenable Zero or more Thenable objects.

jQuery.when() | jQuery API Documentation

とあるように、$.when()の引数はDeferredを取ります。
しかし、sute = validMailSute( inputVal )などは、真偽値を返すので、これがうまく動作しない原因です。

解決方法は、Deferredを返すようにすることかと思います。
なお、jQueryのajax関連のメソッドはおおむねDeferredを返しますので、たいていの場合はそのまま返せば問題ないはずです。

投稿2019/08/19 02:00

Lhankor_Mhy

総合スコア35869

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

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

huyumin

2019/08/19 02:26

ご回答まことにありがとうございます。 まさにその「Deferredを返す」がわからないという状況です。 validMailSute() は検証結果として trueかfalse を返さなくてはならないのに、さらに別に「Deferredを返す」とは、いったいどうやればいいのでしょうか? 下記のように return を2つ書くことになってしまいませんでしょうか? function validMailSute( inputVal ){ var d = $.Deferred(); /*-- 検証のコードは省略 --*/ d.resolve(); return d.promise(); return sute; }
Lhankor_Mhy

2019/08/19 02:59 編集

deferred.then() の引数は、deferred.resolve()の引数を受けます。 たとえば、$.ajax()の戻り値のthen()はそのレスポンスを受けることができるので、その引数を処理すれば問題ないです。 一度ドキュメントを読んでみることをおすすめします。 https://api.jquery.com/deferred.then/ いきなり複雑なコードを書くのが難しいようでしたら、ご提示の問題から離れて、簡単なサンプルを書いて理解を深めてはいかがでしょうか?
huyumin

2019/08/20 11:37 編集

本当に必死でがんばっているのですが、どうしてもわからず、どうか教えてください。 上のご指摘を私なりに咀嚼して、その上でのコードがこちら↓です。 https://jsfiddle.net/y0oawgmz/ かなり進んだと思っていて、 >$.ajax()の戻り値のthen()はそのレスポンスを受ける ということを踏まえて36行目で一緒に戻すようにしています。(こういう意味ですよね?) しかしなぜか、その戻り値を待つことなく、12行目の「.then()」が実行されてしまうのです。 アラートで「step1→step2→step3」と実行したいのですが、step3が先に実行されるということです。 ここだけ解決をご指南いただけませんでしょうか。本当にどうかお願い致します。
Lhankor_Mhy

2019/08/22 00:57

ご解決されて何よりです。
huyumin

2019/08/23 15:41

まだ勉強中ですが、いろいろありがとうございました。
guest

0

ベストアンサー

記入途中で投稿してしまいました、いま追記していますので少々お待ちください
-> 追記おわりました、失礼しました。 Aug 21, 2019 3:06 PM


Deferred, when, then, resolveといったキーワードについての、根本的な理解度を深める必要があると思います。とはいえ私もふだんjQueryは使わないので、今ドキュメントを見た範囲での理解度ですが……

jquery

1/* 2 * arg: Deferredオブジェクト。arg.resolve(foo) の形で、fooをthenのコールバックに渡すことができる 3 * resolvedValue: arg が resolve(foo) したときの foo がここに渡る 4*/ 5$.when(arg).then(function(resolvedValue) { /* ... */}); 6 7/* 8 * arg1 と arg2 の両方が resolve (またはreject) されたら、thenの中が実行される 9 * val1 は arg1.resolve(foo) の foo 10 * val2 は arg2.resolve(bar) の bar が渡る 11*/ 12$.when(arg1, arg2).then(function(val1, val2) { /* ... */});

というように、 $.when() は、引数として渡した Deferredオブジェクト (もちろん、Defeerdオブジェクトを返す関数でも可) のすべてが完了するまで待って、それらの結果を踏まえた処理をやりましょう、というときに使える超便利機能です。Vanilla JS の Promise.all() と同じですね。


validMailSute() は検証結果として trueかfalse を返さなくてはならないのに、さらに別に「Deferredを返す」とは、いったいどうやればいいのでしょうか?

Lhankor_Mhyさんの回答に対する上記のコメントを拝見しました。この疑問に対しての回答は次のとおりです。

jquery

1$.when(validMailSute(emailAddress)).then(isOK => { 2 console.log(`This email is ${isOK? 'OK' : 'NG'}`); 3}) 4 5function validMailSute(emailAddress) { 6 const deferred = $.Deferred(); 7 $.ajax('xxx').then(res => { 8 // resolveするときに「trueかfalse」を持たせると、 9 // 呼び出し元のthenのコールバックに引数として「trueかfalse」を渡すことができる 10 deferred.resolve(true); 11 }); 12 // 関数としては promise() を返す 13 return deferred.promise(); 14}

全体のコードとしては次のような感じかなと思います。
最終形を渡して投げっぱなしにするのは質問者さんのためにはならないと思いますが、すでに「何を理解したほうがいいか」という観点では他の皆様からすばらしい指摘を受けていると思いますので、お手本と言うと烏滸がましいですが、参考になれば幸いです。

jquery

1/** 2 * Check the email address with Validation.pizza 3 * @param {String} email address to test 4 * @return {Deferred} isdisposable? (disposable address = true) 5 */ 6function ajaxForDisposableCheck(addr) { 7 const deferred = $.Deferred(); 8 $.ajax('https://www.validator.pizza/email/' + addr).done(res => { 9 deferred.resolve(res.disposable); 10 }); 11 return deferred.promise(); 12} 13 14/** 15 * @param {String} email address to test 16 * @return {Deferred} isDuplicated? (duped = true) 17 */ 18function ajaxForDuplicationCheck(addr) { 19 return ($.Deferred()).resolve(false); 20} 21 22// メールアドレスの入力 23$(document).on("input","#box", function(){ 24 const mail = $('#box').val(); 25 const reg = /^(([^<>()[]\.,;:\s@"]+(.[^<>()[]\.,;:\s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z\-0-9]+.)+[a-zA-Z]{2,}))$/; 26 if(!reg.test(mail)) { 27 console.error('invalid foramt address'); 28 $('#box').addClass('dame'); 29 return; 30 } 31 32 $.when( 33 ajaxForDisposableCheck(mail), 34 ajaxForDuplicationCheck(mail) 35 ).then((isDisposableAddress, isDuplicatedAddress) => { 36 if(isDisposableAddress || isDuplicatedAddress) { 37 console.error('is disposable?', isDisposableAddress); 38 console.error('is duplicated?', isDuplicatedAddress); 39 $('#box').addClass('dame'); 40 return; 41 } 42 console.log('OK!'); 43 $('#box').removeClass('dame'); 44 }); 45});

投稿2019/08/21 05:47

編集2019/08/21 06:06
thyda.eiqau

総合スコア2982

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

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

huyumin

2019/08/21 14:20

お世話になります!このたびはご回答、本っ当に、どうもありがとうございました。 もう二日以上かけてわからないので、最終形なしでがんばれとは、正直辛いものでした…(じっさい泣いてましたw)本当に感謝です。 もし余力ございましたら、2つ教えて頂けませんでしょうか。 まずこちら↓は、頂いたコードに【B】を加えたものになります。 https://jsfiddle.net/uwq2ar9p/ ――質問1―― 9行目の【A】は頂いたコードで、21行目の【B】はわたしが加えました。機能は同じと思います。 なぜ【B】はalwaysがあると機能しないのでしょうか? ――質問2―― 64行目の「isDisposableAddress, isDuplicatedAddress」はどこから出てきて、なぜ次の条件分岐で使えるのですか? アロー関数でない場合ですとどうなりますでしょう? 以上2つです。ご返信遅くなり夜分遅くに恐れ入ります。お手すきの際にご返信頂けましら幸いです。
thyda.eiqau

2019/08/21 14:49

質問1について 簡単にググった範囲ですが次のブログがありました http://beck23.hatenablog.com/entry/2014/11/08/022842 ここでは "Promiseオブジェクトは、生成されたときはpendingの状態" と紹介されています。状態がresolvedまたはrejectedになったときに完了したと見なされ、完了したと見なされたときにwhen() の then() の中のコールバックが実行されるわけです。ajax().then( Promiseオブジェクトをresolve ).always( Promiseオブジェクトを生成 ) の順番で処理すると、resolvedになる→pendingになるの順番になってしまって、「pendingがresolvedになる」というシーンが永遠に訪れないために、thenのコールバックが実行されないのではないかと思います。 質問2について isDisposableAddress, isDuplicatedAddressは、それぞれajaxForDisposableCheck() ajaxForDuplicationCheck() が返している Deferredオブジェクト (Promiseオブジェクト) がresolveされたときの値を受け取る変数です。resolve(foo) したときのfooをthenのコールバックで受け取れるというのは回答本文にてすでに提示済みです。ここを再度質問されるというのはちょっと厳しいですね。 私も実際、ドキュメントやブログを見たときはまずは流し読みしてコードを書いてみて動かなければ再度読み込むみたいな流れでやることが多いですが、多少長くても頑張って読んでみるようにしたほうがよいです。技術系のブログなんかは性質上内容が重かったり単純に文字数が多くてしんどかったりしますが、頑張って読んで、わからない内容があったらその単語とかでググってみて、モノにしたほうが最終的に自分のためになります。 しつこいようですが私はマジでjQueryは知らないです。fetchやquerySelectorがなかった時代でさえ重くなるのを嫌ってVanilla JSで書いていました。私の回答は30分そこらググって得られた情報と、Vanillaで得ていたPromiseの知見から出てきています。 アロー関数でない場合は $.when(略).then(function(isDisposableAddress, isDuplicatedAddress) { 略 }) です。
thyda.eiqau

2019/08/21 14:50

すげえ説教臭くてワロタ
huyumin

2019/08/21 15:34

>ajax().then( Promiseオブジェクトをresolve ).always( Promiseオブジェクトを生成 ) の順番で処理すると、resolvedになる→pendingになるの順番になってしまって、「pendingがresolvedになる」というシーンが永遠に訪れない... ということはPromiseオブジェクトは生成してからresolveなのですか!? 書いてある順番として上にresolveがあって下に生成があるという流れなので、その流れのままalwaysの中で生成を書いてしまいました。 >回答本文にてすでに提示済みです。 改行が変わって、アロー関数になっているだけで、もうわからなくなっていました。すみません。改めて見てわかりました。 説教臭い?と言えばそうなのかもしれませんが、不快な臭いではまったくないですので、どうぞガシガシ仰ってください。深夜までご返信誠にありがとうございました。本当に助かりました。心の底から大感謝です。m(*- -*)m
Lhankor_Mhy

2019/08/22 01:39 編集

すでにご解決されていますので蛇足になりますが、$.ajax() は、jqXHRというxhrの拡張を返すのですが、実はthenableです。 なので、以下のように書けます。(もちろん、async await でも書けます) const deferred = $.Deferred(); $.ajax('https://www.validator.pizza/email/' + addr).done(res => { deferred.resolve(res.disposable); }); return deferred.promise(); ↓ return $.ajax( 'https://www.validator.pizza/email/' + addr ).then( res => res.disposable );
huyumin

2019/08/23 15:44

const deferred = $.Deferred(); を発行しなくても return $.ajax() とすると、そのajaxの関数(ajaxForDisposableCheck)の呼び出し元(クリックイベント)に対して勝手に return deferred.promise(); を送ってくれるという感じでしょうか?? レベルたかいです。笑
thyda.eiqau

2019/08/23 17:14

いや、勝手にはやってくれないです。自分で書いたとおりにしか動かないです。 私は「whenの引数に$.Deferredを渡せる」を知っていて、「whenの引数に$.ajaxを渡せる」を知りませんでした。なので、whenに渡すためにわざわざ$.Deferredを生成して$.ajaxの結果をresolveで渡すというコードを書いていましたが、実はwhenの引数には$.ajaxを渡すことができるので、そんな回りくどいことしなくても綺麗に書けるよということですね
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問