非同期呼び出しから応答を返すにはどうすればよいですか?
foo
非同期リクエストを行う機能があります。からの応答/結果を返すにはどうすればよいfoo
ですか?
コールバックから値を返し、その結果を関数内のローカル変数に割り当てて返すことを試みましたが、実際には応答を返しません(すべてが返されるundefined
か、変数の初期値が何であれresult
) 。
jQueryのajax
関数を使用した例:
function foo() {
var result;
$.ajax({
url: '...',
success: function(response) {
result = response;
// return response; // <- I tried that one as well
}
});
return result; // It always returns `undefined`
}
node.jsの使用例:
function foo() {
var result;
fs.readFile("path/to/file", function(err, data) {
result = data;
// return data; // <- I tried that one as well
});
return result; // It always returns `undefined`
}
then
promiseのブロックを使用した例:
function foo() {
var result;
fetch(url).then(function(response) {
result = response;
// return response; // <- I tried that one as well
});
return result; // It always returns `undefined`
}
回答
→さまざまな例を使用した非同期動作のより一般的な説明について は、関数内で変数を変更した後、変数が変更されないのはなぜですか?を参照してください。-非同期コードリファレンス
→すでに問題を理解している場合は、以下の可能な解決策にスキップしてください。
問題
AjaxのAは非同期を表します。つまり、要求の送信(または応答の受信)は通常の実行フローから除外されます。あなたの例では、すぐに戻り、コールバックとして渡した関数が呼び出される前に、次のステートメント、が実行されます。$.ajax
return result;
success
これは、同期フローと非同期フローの違いを明確にするアナロジーです。
同期
あなたが友人に電話をかけて、あなたのために何かを探すように彼に頼んだと想像してください。しばらく時間がかかるかもしれませんが、あなたは電話を待って、あなたの友人があなたが必要とする答えをあなたに与えるまで、宇宙を見つめます。
「通常の」コードを含む関数呼び出しを行う場合も同じことが起こります。
function findItem() {
var item;
while(item_not_found) {
// search
}
return item;
}
var item = findItem();
// Do something with item
doSomethingElse();
findItem
実行に時間がかかる場合でも、後続のコードvar item = findItem();
は、関数が結果を返すまで待機する必要があります。
非同期
同じ理由で、もう一度友達に電話します。しかし、今回はあなたが急いでいるので、彼はあなたの携帯電話であなたに電話をかけ直すべきだと彼に言います。あなたは電話を切り、家を出て、あなたがするつもりだったことを何でもします。あなたの友人があなたに電話をかけたら、あなたは彼があなたに与えた情報を扱っています。
それはまさに、Ajaxリクエストを実行するときに起こっていることです。
findItem(function(item) {
// Do something with the item
});
doSomethingElse();
応答を待つ代わりに、実行はすぐに続行され、Ajax呼び出しの後のステートメントが実行されます。最終的に応答を取得するには、応答を受信したときに呼び出される関数、コールバックを提供します(何かに注意してください?コールバック?)。その呼び出しの後に来るステートメントは、コールバックが呼び出される前に実行されます。
解決策
JavaScriptの非同期性を取り入れましょう!特定の非同期操作は同期の対応物を提供しますが(「Ajax」もそうです)、特にブラウザーのコンテキストでは、それらを使用することは一般的に推奨されていません。
なぜ悪いのですか?
JavaScriptはブラウザのUIスレッドで実行され、長時間実行されるプロセスはUIをロックして、応答しなくなります。また、JavaScriptの実行時間には上限があり、ブラウザはユーザーに実行を継続するかどうかを尋ねます。
これはすべて、本当に悪いユーザーエクスペリエンスです。ユーザーは、すべてが正常に機能しているかどうかを判断できなくなります。さらに、接続速度が遅いユーザーの場合、影響はさらに悪化します。
以下では、すべてが互いに積み重なって構築されている3つの異なるソリューションを見ていきます。
- 約束
async/await
(ES2017 +、トランスパイラーまたは再生器を使用している場合は古いブラウザーで利用可能) - コールバック(ノードで人気)
then()
Promise with(ES2015 +、多くのPromiseライブラリの1つを使用している場合は古いブラウザで利用可能)
3つすべてが現在のブラウザ、およびノード7以降で使用できます。
ES2017 +:との約束 async/await
2017年にリリースされたECMAScriptバージョンでは、非同期関数の構文レベルのサポートが導入されました。助けを借りてasync
とawait
、あなたは「同期型」で非同期を書くことができます。コードはまだ非同期ですが、読みやすく、理解しやすいです。
async/await
async
promiseの上に構築されます:関数は常にpromiseを返します。await
promiseを「アンラップ」し、promiseが解決された値になるか、promiseが拒否された場合はエラーをスローします。
重要:関数await
内でのみ使用できasync
ます。現在、トップレベルawait
はまだサポートされていないため、コンテキストを開始するには、非同期IIFE(即時呼び出し関数式)を作成する必要がある場合がありasync
ます。
MDNについてasync
、およびawait
MDNについて詳しく読むことができます。
上記の遅延の上に構築された例を次に示します。
// Using 'superagent' which will return a promise.
var superagent = require('superagent')
// This is isn't declared as `async` because it already returns a promise
function delay() {
// `delay` returns a promise
return new Promise(function(resolve, reject) {
// Only `delay` is able to resolve or reject the promise
setTimeout(function() {
resolve(42); // After 3 seconds, resolve the promise with value 42
}, 3000);
});
}
async function getAllBooks() {
try {
// GET a list of book IDs of the current user
var bookIDs = await superagent.get('/user/books');
// wait for 3 seconds (just for the sake of this example)
await delay();
// GET information about each book
return await superagent.get('/books/ids='+JSON.stringify(bookIDs));
} catch(error) {
// If any of the awaited promises was rejected, this catch block
// would catch the rejection reason
return null;
}
}
// Start an IIFE to use `await` at the top level
(async function(){
let books = await getAllBooks();
console.log(books);
})();
現在のブラウザとノードのバージョンはをサポートしていasync/await
ます。また、再生器(またはBabelなどの再生器を使用するツール)を使用してコードをES5に変換することにより、古い環境をサポートすることもできます。
関数にコールバックを受け入れさせる
コールバックは、関数1が関数2に渡されるときです。関数2は、準備ができているときはいつでも関数1を呼び出すことができます。非同期プロセスのコンテキストでは、非同期プロセスが実行されるたびにコールバックが呼び出されます。通常、結果はコールバックに渡されます。
質問の例ではfoo
、コールバックを受け入れて、それをsuccess
コールバックとして使用できます。したがって、この
var result = foo();
// Code that depends on 'result'
になります
foo(function(result) {
// Code that depends on 'result'
});
ここでは、関数を「インライン」で定義しましたが、任意の関数参照を渡すことができます。
function myCallback(result) {
// Code that depends on 'result'
}
foo(myCallback);
foo
それ自体は次のように定義されます。
function foo(callback) {
$.ajax({
// ...
success: callback
});
}
callback
foo
呼び出すときに渡す関数を参照し、に渡しsuccess
ます。つまり、Ajaxリクエストが成功$.ajax
するとcallback
、応答を呼び出してコールバックに渡します(これはresult
、コールバックを定義した方法であるため、で参照できます)。
応答をコールバックに渡す前に処理することもできます。
function foo(callback) {
$.ajax({
// ...
success: function(response) {
// For example, filter the response
callback(filtered_response);
}
});
}
見た目よりもコールバックを使用してコードを書く方が簡単です。結局のところ、ブラウザーのJavaScriptはイベント駆動型(DOMイベント)です。Ajax応答を受信することは、イベントに他なりません。
サードパーティのコードを使用する必要がある場合は問題が発生する可能性がありますが、ほとんどの問題は、アプリケーションフローを検討するだけで解決できます。
ES2015 +:then()で約束
約束APIは、 ECMAScriptの6(ES2015)の新機能ですが、それは良い持っているブラウザのサポートをすでに。標準のPromisesAPIを実装し、非同期関数(bluebirdなど)の使用と構成を容易にする追加のメソッドを提供するライブラリも多数あります。
Promiseは、将来の価値のコンテナです。promiseが値を受け取る(解決される)か、キャンセルされる(拒否される)と、この値にアクセスしたいすべての「リスナー」に通知します。
プレーンコールバックに対する利点は、コードを分離でき、作成が簡単になることです。
これがpromiseの使用例です。
function delay() {
// `delay` returns a promise
return new Promise(function(resolve, reject) {
// Only `delay` is able to resolve or reject the promise
setTimeout(function() {
resolve(42); // After 3 seconds, resolve the promise with value 42
}, 3000);
});
}
delay()
.then(function(v) { // `delay` returns a promise
console.log(v); // Log the value once it is resolved
})
.catch(function(v) {
// Or do something else if it is rejected
// (it would not happen in this example, since `reject` is not called).
});
Ajax呼び出しに適用すると、次のようなpromiseを使用できます。
function ajax(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
resolve(this.responseText);
};
xhr.onerror = reject;
xhr.open('GET', url);
xhr.send();
});
}
ajax("/echo/json")
.then(function(result) {
// Code depending on result
})
.catch(function() {
// An error occurred
});
promiseが提供するすべての利点を説明することは、この回答の範囲を超えていますが、新しいコードを作成する場合は、それらを真剣に検討する必要があります。それらはあなたのコードの素晴らしい抽象化と分離を提供します。
約束に関する詳細情報:HTML5ロック-JavaScriptの約束
補足:jQueryの遅延オブジェクト
遅延オブジェクトは、jQueryのpromiseのカスタム実装です(Promise APIが標準化される前)。それらはほぼ約束のように動作しますが、わずかに異なるAPIを公開します。
jQueryのすべてのAjaxメソッドは、関数から返すことができる「遅延オブジェクト」(実際には遅延オブジェクトの約束)をすでに返します。
function ajax() {
return $.ajax(...);
}
ajax().done(function(result) {
// Code depending on result
}).fail(function() {
// An error occurred
});
補足:約束の落とし穴
約束と延期されたオブジェクトは将来の価値の単なるコンテナであり、価値そのものではないことに注意してください。たとえば、次のようなものがあるとします。
function checkPassword() {
return $.ajax({
url: '/password',
data: {
username: $('#username').val(),
password: $('#password').val()
},
type: 'POST',
dataType: 'json'
});
}
if (checkPassword()) {
// Tell the user they're logged in
}
このコードは、上記の非同期の問題を誤解しています。具体的に$.ajax()
は、サーバーの「/ password」ページをチェックしている間はコードをフリーズしません。サーバーにリクエストを送信し、待機している間、サーバーからの応答ではなく、jQuery AjaxDeferredオブジェクトをすぐに返します。つまり、if
ステートメントは常にこのDeferredオブジェクトを取得し、それをとして扱いtrue
、ユーザーがログインしているかのように続行します。良くありません。
しかし、修正は簡単です。
checkPassword()
.done(function(r) {
if (r) {
// Tell the user they're logged in
} else {
// Tell the user their password was bad
}
})
.fail(function(x) {
// Tell the user something bad happened
});
推奨されません:同期「Ajax」呼び出し
前述したように、一部の(!)非同期操作には同期操作があります。私はそれらの使用を推奨していませんが、完全を期すために、同期呼び出しを実行する方法を次に示します。
jQueryなし
XMLHttpRequest
オブジェクトを直接使用する場合は、false
3番目の引数として.open
。に渡します。
jQuery
jQueryを使用する場合は、async
オプションをに設定できますfalse
。このオプションは、jQuery1.8以降非推奨になっていることに注意してください。その後、success
コールバックを使用するかresponseText
、jqXHRオブジェクトのプロパティにアクセスできます。
function foo() {
var jqXHR = $.ajax({
//...
async: false
});
return jqXHR.responseText;
}
あなたのような、他のjQueryのAjaxメソッドを使用している場合$.get
、$.getJSON
など、あなたはそれを変更する必要が$.ajax
(あなただけに設定パラメータを渡すことができるため$.ajax
)。
注意喚起!同期JSONPリクエストを行うことはできません。JSONPは、その性質上、常に非同期です(このオプションを考慮しないもう1つの理由)。
コードでjQueryを使用していない場合、この回答はあなたにぴったりです
あなたのコードはこれに沿ったものでなければなりません:
function foo() {
var httpRequest = new XMLHttpRequest();
httpRequest.open('GET', "/echo/json");
httpRequest.send();
return httpRequest.responseText;
}
var result = foo(); // always ends up being 'undefined'
Felix Klingは、jQuery for AJAXを使用している人々のために素晴らしい仕事をしました。私は、そうでない人々のために代替手段を提供することにしました。
(新しいfetch
API、Angular、またはPromiseを使用している場合は、以下に別の回答を追加しました)
あなたが直面していること
これは他の回答からの「問題の説明」の短い要約です。これを読んだ後でわからない場合は、それを読んでください。
AJAXのAは非同期を表します。つまり、要求の送信(または応答の受信)は通常の実行フローから除外されます。あなたの例で.send
は、すぐに戻りreturn result;
、success
コールバックとして渡した関数が呼び出される前に、次のステートメント、が実行されます。
これは、戻ってきたときに、定義したリスナーがまだ実行されていないことを意味します。つまり、戻ってきた値が定義されていません。
これは簡単な例えです
function getFive(){
var a;
setTimeout(function(){
a=5;
},10);
return a;
}
a
返される値はundefined
、a=5
パーツがまだ実行されていないためです。AJAXはこのように動作します。サーバーがブラウザにその値を通知する前に、値を返します。
この問題に対する1つの可能な解決策は、計算が完了したときに何をすべきかをプログラムに指示して、リアクティブにコーディングすることです。
function onComplete(a){ // When the code completes, do this
alert(a);
}
function getFive(whenDone){
var a;
setTimeout(function(){
a=5;
whenDone(a);
},10);
}
これはCPSと呼ばれます。基本的に、getFive
完了時に実行するアクションを渡し、イベントが完了したときにどのように反応するかをコードに指示します(AJAX呼び出し、この場合はタイムアウトなど)。
使用法は次のようになります。
getFive(onComplete);
画面に「5」を警告する必要があります。(フィドル)。
可能な解決策
これを解決するには、基本的に2つの方法があります。
- AJAX呼び出しを同期させます(SJAXと呼びましょう)。
- コールバックで正しく機能するようにコードを再構築します。
1.同期AJAX-やらないでください!!
同期AJAXに関しては、それをしないでください!フェリックスの答えは、なぜそれが悪い考えであるかについていくつかの説得力のある議論を提起します。要約すると、サーバーが応答を返すまでユーザーのブラウザーをフリーズし、非常に悪いユーザーエクスペリエンスを作成します。理由についてMDNから抜粋した別の短い要約を次に示します。
XMLHttpRequestは、同期通信と非同期通信の両方をサポートします。ただし、一般的に、パフォーマンス上の理由から、非同期要求は同期要求よりも優先されます。
要するに、同期リクエストはコードの実行をブロックします......これは深刻な問題を引き起こす可能性があります...
あなたがそれをしなければならないならば、あなたは旗を渡すことができます:これが方法です:
var request = new XMLHttpRequest();
request.open('GET', 'yourURL', false); // `false` makes the request synchronous
request.send(null);
if (request.status === 200) {// That's HTTP for 'ok'
console.log(request.responseText);
}
2.コードを再構築します
関数にコールバックを受け入れさせます。サンプルコードでfoo
は、コールバックを受け入れるように作成できます。完了時にどのように反応するかをコードに指示しますfoo
。
そう:
var result = foo();
// code that depends on `result` goes here
になる:
foo(function(result) {
// code that depends on `result`
});
ここでは無名関数を渡しましたが、既存の関数への参照を簡単に渡すことができ、次のようになります。
function myHandler(result) {
// code that depends on `result`
}
foo(myHandler);
この種のコールバック設計がどのように行われるかの詳細については、Felixの回答を確認してください。
それでは、それに応じて動作するようにfoo自体を定義しましょう
function foo(callback) {
var httpRequest = new XMLHttpRequest();
httpRequest.onload = function(){ // when the request is loaded
callback(httpRequest.responseText);// we're calling our method
};
httpRequest.open('GET', "/echo/json");
httpRequest.send();
}
これで、foo関数がAJAXが正常に完了したときに実行するアクションを受け入れるようになりました。応答ステータスが200でないかどうかを確認し、それに応じて動作することで、これをさらに拡張できます(失敗ハンドラーなどを作成します)。私たちの問題を効果的に解決します。
それでもこれを理解するのに苦労している場合は、MDNのAJAXスタートガイドをお読みください。
XMLHttpRequestの2(最初のすべての答えから読みベンジャミンGruenbaum&フェリックスクリングを)
jQueryを使用せず、最新のブラウザーとモバイルブラウザーで機能する短いXMLHttpRequest 2が必要な場合は、次のように使用することをお勧めします。
function ajax(a, b, c){ // URL, callback, just a placeholder
c = new XMLHttpRequest;
c.open('GET', a);
c.onload = b;
c.send()
}
ご覧のように:
- リストされている他のすべての関数よりも短いです。
- コールバックは直接設定されます(したがって、余分な不要なクロージャはありません)。
- 新しいオンロードを使用します(したがって、readystate &&ステータスを確認する必要はありません)
- XMLHttpRequest 1を煩わしくする、覚えていない状況が他にもいくつかあります。
このAjax呼び出しの応答を取得する方法は2つあります(3つはXMLHttpRequest変数名を使用します)。
最も簡単な:
this.response
または、何らかの理由でbind()
クラスへのコールバックを行った場合:
e.target.response
例:
function callback(e){
console.log(this.response);
}
ajax('URL', callback);
または(上記の方が良い匿名関数は常に問題です):
ajax('URL', function(e){console.log(this.response)});
簡単なことはありません。
これで、onreadystatechangeまたはXMLHttpRequest変数名を使用する方が良いと言う人もいるかもしれません。それは間違っている。
XMLHttpRequestの高度な機能を確認してください
すべての*最新のブラウザをサポートしていました。そして、XMLHttpRequest 2が存在するため、このアプローチを使用していることを確認できます。使用しているすべてのブラウザで問題が発生したことはありません。
onreadystatechangeは、状態2のヘッダーを取得する場合にのみ役立ちます。
XMLHttpRequest
変数名の使用は、onload / oreadystatechangeクロージャ内でコールバックを実行する必要があるため、もう1つの大きなエラーです。そうしないと、変数名が失われます。
postとFormDataを使用してより複雑なものが必要な場合は、この関数を簡単に拡張できます。
function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val},placeholder
c = new XMLHttpRequest;
c.open(e||'get', a);
c.onload = b;
c.send(d||null)
}
繰り返しますが...これは非常に短い関数ですが、取得して投稿します。
使用例:
x(url, callback); // By default it's get so no need to set
x(url, callback, 'post', {'key': 'val'}); // No need to set post data
または、完全なフォーム要素(document.getElementsByTagName('form')[0]
)を渡します。
var fd = new FormData(form);
x(url, callback, 'post', fd);
または、いくつかのカスタム値を設定します。
var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);
ご覧のとおり、同期を実装していません...それは悪いことです。
そうは言っても...簡単な方法でやってみませんか?
コメントで述べたように、エラー&&同期の使用は、答えのポイントを完全に壊します。Ajaxを適切な方法で使用するための良い短い方法はどれですか?
エラーハンドラ
function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val}, placeholder
c = new XMLHttpRequest;
c.open(e||'get', a);
c.onload = b;
c.onerror = error;
c.send(d||null)
}
function error(e){
console.log('--Error--', this.type);
console.log('this: ', this);
console.log('Event: ', e)
}
function displayAjax(e){
console.log(e, this);
}
x('WRONGURL', displayAjax);
上記のスクリプトには、関数を危険にさらさないように静的に定義されたエラーハンドラーがあります。エラーハンドラは他の関数にも使用できます。
しかし、実際にエラーを発生させる唯一の方法は、間違ったURLを書き込むことです。その場合、すべてのブラウザーがエラーをスローします。
カスタムヘッダーを設定したり、responseTypeをblob配列バッファーに設定したりする場合は、エラーハンドラーが役立つ可能性があります...
メソッドとして「POSTAPAPAP」を渡してもエラーは発生しません。
'fdggdgilfdghfldj'をformdataとして渡しても、エラーはスローされません。
最初のケースでは、エラーはdisplayAjax()
アンダーの中this.statusText
にありMethod not Allowed
ます。
2番目のケースでは、それは単に機能します。正しいPOSTデータを渡したかどうかをサーバー側で確認する必要があります。
クロスドメインは許可されていませんが、エラーを自動的にスローします。
エラー応答には、エラーコードはありません。
this.type
エラーに設定されているものだけがあります。
エラーを完全に制御できないのに、なぜエラーハンドラーを追加するのですか?ほとんどのエラーは、コールバック関数のこの内部で返されますdisplayAjax()
。
したがって、URLを適切にコピーして貼り付けることができれば、エラーチェックは必要ありません。;)
PS:最初のテストとしてx( 'x'、displayAjax)...を書きましたが、完全に応答がありました... ??? そこで、HTMLが配置されているフォルダーを確認したところ、「x.xml」というファイルがありました。したがって、ファイルの拡張子を忘れた場合でも、XMLHttpRequest2はそれを見つけます。私は大爆笑だ
同期ファイルの読み取り
そうしないでください。
しばらくの間ブラウザをブロックしたい場合は、.txt
同期して大きなファイルをロードしてください。
function omg(a, c){ // URL
c = new XMLHttpRequest;
c.open('GET', a, true);
c.send();
return c; // Or c.response
}
今、あなたはすることができます
var res = omg('thisIsGonnaBlockThePage.txt');
非同期でこれを行う方法は他にありません。(ええ、setTimeoutループで...しかし真剣に?)
もう1つのポイントは、APIや独自のリストのファイル、またはリクエストごとに常に異なる関数を使用するものを使用する場合です。
常に同じXML / JSONをロードするページがある場合、または1つの関数のみが必要な場合のみ。その場合は、Ajax関数を少し変更し、bを特別な関数に置き換えます。
上記の機能は基本的なものです。
関数を拡張したい場合...
はい、できます。
私は多くのAPIを使用しており、すべてのHTMLページに統合する最初の関数の1つは、この回答の最初のAjax関数であり、GETのみです...
しかし、XMLHttpRequest2では多くのことができます。
ダウンロードマネージャー(履歴書、ファイルリーダー、ファイルシステムで両側の範囲を使用)、キャンバスを使用したさまざまな画像リサイザーコンバーター、base64imagesなどをWeb SQLデータベースに入力しました...しかし、これらの場合は、そのための関数のみを作成する必要があります目的...場合によっては、blob、配列バッファーが必要になります。ヘッダーを設定したり、mimetypeをオーバーライドしたりできますが、他にもたくさんあります...
しかし、ここでの問題は、Ajax応答を返す方法です...(簡単な方法を追加しました。)
あなたが約束を使用しているなら、この答えはあなたのためです。
これは、AngularJS、jQuery(遅延あり)、ネイティブXHRの置換(フェッチ)、EmberJS、BackboneJSの保存、またはpromiseを返す任意のノードライブラリを意味します。
あなたのコードはこれに沿ったものでなければなりません:
function foo() {
var data;
// or $.get(...).then, or request(...).then, or query(...).then
fetch("/echo/json").then(function(response){
data = response.json();
});
return data;
}
var result = foo(); // result is always undefined no matter what.
Felix Klingは、AJAXのコールバックでjQueryを使用している人々のために答えを書くという素晴らしい仕事をしました。ネイティブXHRに対する答えがあります。この回答は、フロントエンドまたはバックエンドでのpromiseの一般的な使用法に関するものです。
中心的な問題
ブラウザとNodeJS / io.jsを使用するサーバーのJavaScript同時実行モデルは、非同期でリアクティブです。
promiseを返すメソッドを呼び出すときはいつでも、then
ハンドラーは常に非同期で実行されます。つまり、ハンドラーにないその下のコードの後に実行されます.then
。
これは、定義しdata
たthen
ハンドラーを返すときに、まだ実行されていないことを意味します。これは、返される値が時間内に正しい値に設定されていないことを意味します。
この問題の簡単な例えは次のとおりです。
function getFive(){
var data;
setTimeout(function(){ // set a timer for one second in the future
data = 5; // after a second, do this
}, 1000);
return data;
}
document.body.innerHTML = getFive(); // `undefined` here and not 5
の値data
はundefined
、data = 5
パーツがまだ実行されていないためです。1秒で実行される可能性がありますが、その時点では戻り値とは無関係です。
操作(AJAX、サーバー呼び出し、IO、タイマー)はまだ行われていないため、リクエストがコードにその値を伝える機会を得る前に、値を返しています。
この問題に対する1つの可能な解決策は、計算が完了したときに何をすべきかをプログラムに指示して、リアクティブにコーディングすることです。Promiseは、本質的に時間的(時間に敏感)であることにより、これを積極的に可能にします。
約束の簡単な要約
Promiseは、時間の経過に伴う値です。約束には状態があり、価値のない保留中として開始され、次のように解決できます。
- 計算が正常に完了したことを意味します。
- 拒否されたということは、計算が失敗したことを意味します。
プロミスは一度だけ状態を変更でき、その後は常に同じ状態に永久に留まります。then
ハンドラーをpromiseにアタッチして、その値を抽出し、エラーを処理できます。then
ハンドラーを使用すると、呼び出しを連鎖させることができます。Promiseは、それらを返すAPIを使用して作成されます。たとえば、より最新のAJAX置換fetch
またはjQueryの$.get
リターンプロミス。
.then
約束を呼び出してそこから何かを返すと、処理された値の約束が得られます。別の約束を返すと素晴らしいものが手に入りますが、馬を抱きしめましょう。
約束を持って
上記の問題をpromiseで解決する方法を見てみましょう。まず、遅延関数を作成するためにPromiseコンストラクターを使用して、上からのpromise状態の理解を示しましょう。
function delay(ms){ // takes amount of milliseconds
// returns a new promise
return new Promise(function(resolve, reject){
setTimeout(function(){ // when the time is up
resolve(); // change the promise to the fulfilled state
}, ms);
});
}
これで、setTimeoutをpromiseを使用するように変換した後then
、それをカウントするために使用できます。
function delay(ms){ // takes amount of milliseconds
// returns a new promise
return new Promise(function(resolve, reject){
setTimeout(function(){ // when the time is up
resolve(); // change the promise to the fulfilled state
}, ms);
});
}
function getFive(){
// we're RETURNING the promise, remember, a promise is a wrapper over our value
return delay(100).then(function(){ // when the promise is ready
return 5; // return the value 5, promises are all about return values
})
}
// we _have_ to wrap it like this in the call site, we can't access the plain value
getFive().then(function(five){
document.body.innerHTML = five;
});
基本的には、代わりに返すの値ために同時実行モデルの私たちが行うことはできません-私たちは戻っているラッパーたちができることを値のアンラップとをthen
。それはあなたが開くことができる箱のようなものですthen
。
これを適用する
これは、元のAPI呼び出しでも同じです。次のことができます。
function foo() {
// RETURN the promise
return fetch("/echo/json").then(function(response){
return response.json(); // process it inside the `then`
});
}
foo().then(function(response){
// access the value inside the `then`
})
したがって、これも同様に機能します。すでに非同期の呼び出しから値を返すことはできないことを学びましたが、promiseを使用し、それらをチェーンして処理を実行することはできます。これで、非同期呼び出しから応答を返す方法がわかりました。
ES2015(ES6)
ES6には、途中で戻って元のポイントに戻ることができる関数であるジェネレーターが導入されています。これは通常、次のようなシーケンスに役立ちます。
function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
yield 1;
yield 2;
while(true) yield 3;
}
反復可能なシーケンスに対してイテレータを返す関数です1,2,3,3,3,3,....
。これはそれ自体が興味深いものであり、多くの可能性の余地がありますが、特に興味深いケースが1つあります。
生成しているシーケンスが数値ではなくアクションのシーケンスである場合、アクションが生成されるたびに関数を一時停止し、それを待ってから関数を再開できます。したがって、一連の数値の代わりに、一連の将来の値、つまり、promiseが必要です。
このややトリッキーですが非常に強力なトリックにより、非同期コードを同期的に記述できます。これを行う「ランナー」がいくつかあります。1つを書くのは数行の短いコードですが、この回答の範囲を超えています。Promise.coroutine
ここではBluebirdを使用しますが、co
またはのような他のラッパーがありますQ.async
。
var foo = coroutine(function*(){
var data = yield fetch("/echo/json"); // notice the yield
// code here only executes _after_ the request is done
return data.json(); // data is defined
});
このメソッドは、他のコルーチンから消費できるpromise自体を返します。例えば:
var main = coroutine(function*(){
var bar = yield foo(); // wait our earlier coroutine, it returns a promise
// server call done here, code below executes when done
var baz = yield fetch("/api/users/"+bar.userid); // depends on foo's result
console.log(baz); // runs after both requests done
});
main();
ES2016(ES7)
ES7では、これはさらに標準化されており、現在いくつかの提案がありますが、それらすべてでawait
約束できます。これは、async
andawait
キーワードを追加することによる、上記のES6提案の単なる「砂糖」(より適切な構文)です。上記の例を作成します。
async function foo(){
var data = await fetch("/echo/json"); // notice the await
// code here only executes _after_ the request is done
return data.json(); // data is defined
}
それでも同じ約束を返します:)
Ajaxを誤って使用しています。アイデアは、何も返さないようにすることではなく、代わりに、データを処理するコールバック関数と呼ばれるものにデータを渡します。
あれは:
function handleData( responseData ) {
// Do what you want with the data
console.log(responseData);
}
$.ajax({
url: "hi.php",
...
success: function ( data, status, XHR ) {
handleData(data);
}
});
送信ハンドラーで何かを返しても何も起こりません。代わりに、データを渡すか、成功関数内で直接データを使用して必要な処理を行う必要があります。
最も簡単な解決策は、JavaScript関数を作成し、それをAjaxsuccess
コールバック用に呼び出すことです。
function callServerAsync(){
$.ajax({
url: '...',
success: function(response) {
successCallback(response);
}
});
}
function successCallback(responseObj){
// Do something like read the response and show data
alert(JSON.stringify(responseObj)); // Only applicable to JSON response
}
function foo(callback) {
$.ajax({
url: '...',
success: function(response) {
return callback(null, response);
}
});
}
var result = foo(function(err, result){
if (!err)
console.log(result);
});
恐ろしい手描きの漫画でお答えします。2番目の画像は、コード例にある理由result
ですundefined
。
Angular1
AngularJSを使用している場合は、を使用してこの状況を処理できPromises
ます。
ここにそれは言う、
Promiseは、非同期関数をアンネストするために使用でき、複数の関数をチェーン化できます。
下記のドキュメントにある例。
promiseB = promiseA.then(
function onSuccess(result) {
return result + 1;
}
,function onError(err) {
//Handle error
}
);
// promiseB will be resolved immediately after promiseA is resolved
// and its value will be the result of promiseA incremented by 1.
Angular2以降
ではAngular2
、次の例を見てと、その推奨使用するObservables
とAngular2
。
search(term: string) {
return this.http
.get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)
.map((response) => response.json())
.toPromise();
}
あなたはそれをこのように消費することができます、
search() {
this.searchService.search(this.searchField.value)
.then((result) => {
this.result = result.artists.items;
})
.catch((error) => console.error(error));
}
こちらの元の投稿をご覧ください。ただし、Typescriptはネイティブのes6 Promisesをサポートしていません。使用する場合は、そのためのプラグインが必要になる場合があります。
ここでの回答のほとんどは、単一の非同期操作がある場合に役立つ提案を提供しますが、配列または他のリストのような構造の各エントリに対して非同期操作を実行する必要がある場合に、これが発生することがあります。誘惑はこれをすることです:
// WRONG
var results = [];
theArray.forEach(function(entry) {
doSomethingAsync(entry, function(result) {
results.push(result);
});
});
console.log(results); // E.g., using them, returning them, etc.
例:
// WRONG
var theArray = [1, 2, 3];
var results = [];
theArray.forEach(function(entry) {
doSomethingAsync(entry, function(result) {
results.push(result);
});
});
console.log("Results:", results); // E.g., using them, returning them, etc.
function doSomethingAsync(value, callback) {
console.log("Starting async operation for " + value);
setTimeout(function() {
console.log("Completing async operation for " + value);
callback(value * 2);
}, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
max-height: 100% !important;
}
動作しない理由はdoSomethingAsync
、結果を使用しようとしている時点では、からのコールバックがまだ実行されていないためです。
したがって、配列(またはある種のリスト)があり、エントリごとに非同期操作を実行する場合は、2つのオプションがあります。操作を並列(オーバーラップ)または直列(順番に)で実行します。
平行
それらすべてを開始し、予想されるコールバックの数を追跡し、その数のコールバックを取得したときに結果を使用できます。
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
doSomethingAsync(entry, function(result) {
results[index] = result;
if (--expecting === 0) {
// Done!
console.log("Results:", results); // E.g., using the results
}
});
});
例:
var theArray = [1, 2, 3];
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
doSomethingAsync(entry, function(result) {
results[index] = result;
if (--expecting === 0) {
// Done!
console.log("Results:", results); // E.g., using the results
}
});
});
function doSomethingAsync(value, callback) {
console.log("Starting async operation for " + value);
setTimeout(function() {
console.log("Completing async operation for " + value);
callback(value * 2);
}, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
max-height: 100% !important;
}
(私たちは廃止しexpecting
て使用することもできますresults.length === theArray.length
が、それtheArray
は通話が未解決の間に変更される可能性に私たちを開いたままにします...)
結果が順不同で到着した場合でも、index
fromを使用して、関連するエントリと同じ位置にforEach
結果を保存する方法に注意してくださいresults
(非同期呼び出しは必ずしも開始された順序で完了するとは限らないため)。
しかし、関数からこれらの結果を返す必要がある場合はどうでしょうか。他の答えが指摘しているように、あなたはできません。関数にコールバックを受け入れて呼び出す(またはPromiseを返す)必要があります。コールバックバージョンは次のとおりです。
function doSomethingWith(theArray, callback) {
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
doSomethingAsync(entry, function(result) {
results[index] = result;
if (--expecting === 0) {
// Done!
callback(results);
}
});
});
}
doSomethingWith(theArray, function(results) {
console.log("Results:", results);
});
例:
function doSomethingWith(theArray, callback) {
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
doSomethingAsync(entry, function(result) {
results[index] = result;
if (--expecting === 0) {
// Done!
callback(results);
}
});
});
}
doSomethingWith([1, 2, 3], function(results) {
console.log("Results:", results);
});
function doSomethingAsync(value, callback) {
console.log("Starting async operation for " + value);
setTimeout(function() {
console.log("Completing async operation for " + value);
callback(value * 2);
}, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
max-height: 100% !important;
}
または、Promise
代わりに次のバージョンが返されます。
function doSomethingWith(theArray) {
return new Promise(function(resolve) {
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
doSomethingAsync(entry, function(result) {
results[index] = result;
if (--expecting === 0) {
// Done!
resolve(results);
}
});
});
});
}
doSomethingWith(theArray).then(function(results) {
console.log("Results:", results);
});
もちろん、doSomethingAsync
エラーが渡された場合reject
は、エラーが発生したときに約束を拒否するために使用します。)
例:
function doSomethingWith(theArray) {
return new Promise(function(resolve) {
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
doSomethingAsync(entry, function(result) {
results[index] = result;
if (--expecting === 0) {
// Done!
resolve(results);
}
});
});
});
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", results);
});
function doSomethingAsync(value, callback) {
console.log("Starting async operation for " + value);
setTimeout(function() {
console.log("Completing async operation for " + value);
callback(value * 2);
}, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
max-height: 100% !important;
}
(または、代わりに、doSomethingAsync
promiseを返すラッパーを作成してから、以下を実行することもできます...)
場合はdoSomethingAsync
、あなたに与えられる約束を、あなたが使用することができますPromise.all
:
function doSomethingWith(theArray) {
return Promise.all(theArray.map(function(entry) {
return doSomethingAsync(entry);
}));
}
doSomethingWith(theArray).then(function(results) {
console.log("Results:", results);
});
doSomethingAsync
2番目と3番目の引数を無視することがわかっている場合は、直接渡すことができますmap
(map
3つの引数を使用してコールバックを呼び出しますが、ほとんどの人は最初の引数のみを使用します)。
function doSomethingWith(theArray) {
return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith(theArray).then(function(results) {
console.log("Results:", results);
});
例:
function doSomethingWith(theArray) {
return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", results);
});
function doSomethingAsync(value) {
console.log("Starting async operation for " + value);
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Completing async operation for " + value);
resolve(value * 2);
}, Math.floor(Math.random() * 200));
});
}
.as-console-wrapper {
max-height: 100% !important;
}
注Promise.all
その約束をそれらが全て解決されたとき、あなたがそれを与える約束のすべての結果の配列との約束を解決する、または拒否したときに最初にあなたはそれを拒否与える約束の。
シリーズ
操作を並行させたくないとしましょう。それらを次々に実行したい場合は、次の操作を開始する前に、各操作が完了するのを待つ必要があります。これを実行し、結果を使用してコールバックを呼び出す関数の例を次に示します。
function doSomethingWith(theArray, callback) {
var results = [];
doOne(0);
function doOne(index) {
if (index < theArray.length) {
doSomethingAsync(theArray[index], function(result) {
results.push(result);
doOne(index + 1);
});
} else {
// Done!
callback(results);
}
}
}
doSomethingWith(theArray, function(results) {
console.log("Results:", results);
});
(私たちは連続して作業を行っているのでresults.push(result)
、順序が狂うことはないことがわかっているので、そのまま使用できます。上記では使用できましたがresults[index] = result;
、次の例の一部ではインデックスがありません。使用します。)
例:
function doSomethingWith(theArray, callback) {
var results = [];
doOne(0);
function doOne(index) {
if (index < theArray.length) {
doSomethingAsync(theArray[index], function(result) {
results.push(result);
doOne(index + 1);
});
} else {
// Done!
callback(results);
}
}
}
doSomethingWith([1, 2, 3], function(results) {
console.log("Results:", results);
});
function doSomethingAsync(value, callback) {
console.log("Starting async operation for " + value);
setTimeout(function() {
console.log("Completing async operation for " + value);
callback(value * 2);
}, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
max-height: 100% !important;
}
(または、もう一度、そのためのラッパーを作成しdoSomethingAsync
て、約束を与え、以下を実行します...)
場合はdoSomethingAsync
、あなたに約束を与えるあなたは(おそらくのようなtranspilerとES2017 +構文を使用することができれば、バベル)は、使用することができますasync
機能をとfor-of
してawait
:
async function doSomethingWith(theArray) {
const results = [];
for (const entry of theArray) {
results.push(await doSomethingAsync(entry));
}
return results;
}
doSomethingWith(theArray).then(results => {
console.log("Results:", results);
});
例:
async function doSomethingWith(theArray) {
const results = [];
for (const entry of theArray) {
results.push(await doSomethingAsync(entry));
}
return results;
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", results);
});
function doSomethingAsync(value) {
console.log("Starting async operation for " + value);
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Completing async operation for " + value);
resolve(value * 2);
}, Math.floor(Math.random() * 200));
});
}
.as-console-wrapper {
max-height: 100% !important;
}
ES2017 +構文を(まだ)使用できない場合は、「Promise reduce」パターンのバリエーションを使用できます(これは、結果を次の1つに渡さないため、通常のPromise reduceよりも複雑ですが、代わりに結果を配列にまとめる):
function doSomethingWith(theArray) {
return theArray.reduce(function(p, entry) {
return p.then(function(results) {
return doSomethingAsync(entry).then(function(result) {
results.push(result);
return results;
});
});
}, Promise.resolve([]));
}
doSomethingWith(theArray).then(function(results) {
console.log("Results:", results);
});
例:
function doSomethingWith(theArray) {
return theArray.reduce(function(p, entry) {
return p.then(function(results) {
return doSomethingAsync(entry).then(function(result) {
results.push(result);
return results;
});
});
}, Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", results);
});
function doSomethingAsync(value) {
console.log("Starting async operation for " + value);
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Completing async operation for " + value);
resolve(value * 2);
}, Math.floor(Math.random() * 200));
});
}
.as-console-wrapper {
max-height: 100% !important;
}
...これはES2015 +の矢印関数ではそれほど面倒ではありません:
function doSomethingWith(theArray) {
return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
results.push(result);
return results;
})), Promise.resolve([]));
}
doSomethingWith(theArray).then(results => {
console.log("Results:", results);
});
例:
function doSomethingWith(theArray) {
return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
results.push(result);
return results;
})), Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", results);
});
function doSomethingAsync(value) {
console.log("Starting async operation for " + value);
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Completing async operation for " + value);
resolve(value * 2);
}, Math.floor(Math.random() * 200));
});
}
.as-console-wrapper {
max-height: 100% !important;
}
この例を見てください:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,$http) {
var getJoke = function(){
return $http.get('http://api.icndb.com/jokes/random').then(function(res){
return res.data.value;
});
}
getJoke().then(function(res) {
console.log(res.joke);
});
});
ご覧のとおり、解決済みのpromisegetJoke
が返されます(返されるときに解決されます)。したがって、$ http.getリクエストが完了するまで待ってから、console.log(res.joke)が実行されます(通常の非同期フローとして)。res.data.value
これはplnkrです:
http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/
ES6ウェイ(非同期-待機)
(function(){
async function getJoke(){
let response = await fetch('http://api.icndb.com/jokes/random');
let data = await response.json();
return data.value;
}
getJoke().then((joke) => {
console.log(joke);
});
})();
これは、多くの新しいJavaScriptフレームワークで使用されているデータバインディングまたはストアの概念が2つの方法でうまく機能する場所の1つです...
したがって、Angular、React、またはデータバインディングまたはストアコンセプトの2つの方法を実行するその他のフレームワークを使用している場合、この問題は簡単に修正されます。つまり、簡単に言えば、結果はundefined
最初の段階にあるため、result = undefined
受信する前に取得できます。データ、結果を取得するとすぐに更新され、Ajax呼び出しの応答が新しい値に割り当てられます...
しかし、この質問で尋ねたように、たとえば純粋なjavascriptまたはjQueryでそれをどのように行うことができますか?
あなたは使用することができ、コールバック、約束最近、観察を、私たちは次のようにいくつかの機能を持っている約束で、たとえば、あなたのためにそれを処理するために、success()
またはthen()
あなたのデータはあなたのための準備ができたときに実行される、コールバックと同じまたはサブスクライブに機能を観察できます。
たとえば、jQueryを使用している場合は、次のようにすることができます。
$(document).ready(function(){
function foo() {
$.ajax({url: "api/data", success: function(data){
fooDone(data); //after we have data, we pass it to fooDone
}});
};
function fooDone(data) {
console.log(data); //fooDone has the data and console.log it
};
foo(); //call happens here
});
この非同期処理を行うための新しい方法であるpromiseとobservableについての詳細を調べてください。
これは、JavaScriptの「謎」に苦しんでいるときに直面する非常に一般的な問題です。今日はこの謎を解き明かしてみましょう。
簡単なJavaScript関数から始めましょう:
function foo(){
// do something
return 'wohoo';
}
let bar = foo(); // bar is 'wohoo' here
これは単純な同期関数呼び出しであり(コードの各行は、次の行の前に「ジョブで終了」します)、結果は期待どおりです。
次に、関数に少し遅延を導入して、コードのすべての行が順番に「終了」しないように、少しひねりを加えましょう。したがって、関数の非同期動作をエミュレートします。
function foo(){
setTimeout( ()=>{
return 'wohoo';
}, 1000 )
}
let bar = foo() // bar is undefined here
さて、その遅延は私たちが期待していた機能を壊してしまいました!しかし、正確には何が起こったのでしょうか?コードを見ると、実際にはかなり論理的です。関数foo()
は、実行時に何も返しません(したがって、戻り値はですundefined
)が、タイマーを開始します。タイマーは、1秒後に関数を実行して「wohoo」を返します。しかし、ご覧のとおり、barに割り当てられる値は、foo()からすぐに返されるものであり、これは何も、つまり単なるundefined
。です。
では、この問題にどのように取り組むのでしょうか。
関数にPROMISEを要求してみましょう。Promiseは、実際にはそれが何を意味するかについてです。つまり、関数は、将来取得する出力を提供することを保証します。それで、上記の小さな問題に対して実際に動作するのを見てみましょう:
function foo(){
return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
setTimeout ( function(){
// promise is RESOLVED , when execution reaches this line of code
resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
}, 1000 )
})
}
let bar ;
foo().then( res => {
bar = res;
console.log(bar) // will print 'wohoo'
});
したがって、要約すると、ajaxベースの呼び出しなどの非同期関数に取り組むためにresolve
、値へのpromiseを使用できます(これを返す予定です)。したがって、要するに、非同期関数では、を返す代わりに値を解決します。
UPDATE(async / awaitでの約束)
then/catch
約束を処理するために使用する以外に、もう1つのアプローチがあります。アイデアがすることです非同期関数を認識して、約束を待つ解決するには、コードの次の行に移動する前に。それはまだpromises
内部にありますが、構文上のアプローチが異なります。物事をより明確にするために、以下の比較を見つけることができます:
then / catchバージョン:
function saveUsers(){
getUsers()
.then(users => {
saveSomewhere(users);
})
.catch(err => {
console.error(err);
})
}
非同期/待機バージョン:
async function saveUsers(){
try{
let users = await getUsers()
saveSomewhere(users);
}
catch(err){
console.error(err);
}
}
非同期関数から値を返す別のアプローチは、非同期関数からの結果を格納するオブジェクトを渡すことです。
これは同じ例です:
var async = require("async");
// This wires up result back to the caller
var result = {};
var asyncTasks = [];
asyncTasks.push(function(_callback){
// some asynchronous operation
$.ajax({
url: '...',
success: function(response) {
result.response = response;
_callback();
}
});
});
async.parallel(asyncTasks, function(){
// result is available after performing asynchronous operation
console.log(result)
console.log('Done');
});
result
非同期操作中に値を格納するためにオブジェクトを使用しています。これにより、非同期ジョブの後でも結果を利用できるようになります。
私はこのアプローチをよく使用します。連続するモジュールを介して結果を配線する場合に、このアプローチがどのように機能するかを知りたいと思います。
約束とコールバックは多くの状況でうまく機能しますが、次のようなことを表現するのは後部の苦痛です。
if (!name) {
name = async1();
}
async2(name);
あなたは通過することになりasync1
ます; name
が未定義かどうかを確認し、それに応じてコールバックを呼び出します。
async1(name, callback) {
if (name)
callback(name)
else {
doSomething(callback)
}
}
async1(name, async2)
小さな例では問題ありませんが、同様のケースやエラー処理が多数含まれていると、煩わしくなります。
Fibers
問題の解決に役立ちます。
var Fiber = require('fibers')
function async1(container) {
var current = Fiber.current
var result
doSomething(function(name) {
result = name
fiber.run()
})
Fiber.yield()
return result
}
Fiber(function() {
var name
if (!name) {
name = async1()
}
async2(name)
// Make any number of async calls from here
}
私が書いた次の例は、
- 非同期HTTP呼び出しを処理します。
- 各API呼び出しからの応答を待ちます。
- Promiseパターンを使用します。
- Promise.allパターンを使用して、複数のHTTP呼び出しに参加します。
この実用的な例は自己完結型です。ウィンドウXMLHttpRequest
オブジェクトを使用して呼び出しを行う単純なリクエストオブジェクトを定義します。一連のpromiseが完了するのを待つ単純な関数を定義します。
環境。この例では、特定のクエリ文字列のセットのオブジェクトを検索するために、Spotify WebAPIエンドポイントplaylist
をクエリしています。
[
"search?type=playlist&q=%22doom%20metal%22",
"search?type=playlist&q=Adele"
]
アイテムごとに、新しいPromiseがブロックを起動しExecutionBlock
、結果を解析し、結果の配列、つまりSpotifyuser
オブジェクトのリストに基づいて新しいPromiseのセットをスケジュールし、ExecutionProfileBlock
非同期で新しいHTTP呼び出しを実行します。
次に、ネストされたPromise構造を確認できます。これにより、複数の完全に非同期のネストされたHTTP呼び出しを生成し、を介して呼び出しの各サブセットからの結果を結合できますPromise.all
。
注
最近のSpotifyAPIsearch
では、リクエストヘッダーでアクセストークンを指定する必要があります。
-H "Authorization: Bearer {your access token}"
したがって、次の例を実行するには、アクセストークンをリクエストヘッダーに配置する必要があります。
var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
log: function(s) {
document.getElementById("console").innerHTML += s + "<br/>"
}
}
// Simple XMLHttpRequest
// based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
call: function(what, response) {
var request;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
request = new XMLHttpRequest();
} else if (window.ActiveXObject) { // Internet Explorer
try {
request = new ActiveXObject('Msxml2.XMLHTTP');
}
catch (e) {
try {
request = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {}
}
}
// State changes
request.onreadystatechange = function() {
if (request.readyState === 4) { // Done
if (request.status === 200) { // Complete
response(request.responseText)
}
else
response();
}
}
request.open('GET', what, true);
request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
request.send(null);
}
}
//PromiseAll
var promiseAll = function(items, block, done, fail) {
var self = this;
var promises = [],
index = 0;
items.forEach(function(item) {
promises.push(function(item, i) {
return new Promise(function(resolve, reject) {
if (block) {
block.apply(this, [item, index, resolve, reject]);
}
});
}(item, ++index))
});
Promise.all(promises).then(function AcceptHandler(results) {
if (done) done(results);
}, function ErrorHandler(error) {
if (fail) fail(error);
});
}; //promiseAll
// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
var url = "https://api.spotify.com/v1/"
url += item;
console.log( url )
SimpleRequest.call(url, function(result) {
if (result) {
var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
return item.owner.href;
})
resolve(profileUrls);
}
else {
reject(new Error("call error"));
}
})
}
arr = [
"search?type=playlist&q=%22doom%20metal%22",
"search?type=playlist&q=Adele"
]
promiseAll(arr, function(item, index, resolve, reject) {
console.log("Making request [" + index + "]")
ExecutionBlock(item, index, resolve, reject);
}, function(results) { // Aggregated results
console.log("All profiles received " + results.length);
//console.log(JSON.stringify(results[0], null, 2));
///// promiseall again
var ExecutionProfileBlock = function(item, index, resolve, reject) {
SimpleRequest.call(item, function(result) {
if (result) {
var obj = JSON.parse(result);
resolve({
name: obj.display_name,
followers: obj.followers.total,
url: obj.href
});
} //result
})
} //ExecutionProfileBlock
promiseAll(results[0], function(item, index, resolve, reject) {
//console.log("Making request [" + index + "] " + item)
ExecutionProfileBlock(item, index, resolve, reject);
}, function(results) { // aggregated results
console.log("All response received " + results.length);
console.log(JSON.stringify(results, null, 2));
}
, function(error) { // Error
console.log(error);
})
/////
},
function(error) { // Error
console.log(error);
});
<div id="console" />
簡単に言うと、次のようなコールバックを実装する必要があります。
function callback(response) {
// Here you can do what ever you want with the response object.
console.log(response);
}
$.ajax({
url: "...",
success: callback
});
2017年の回答:現在のすべてのブラウザーとノードで、必要なことを正確に実行できるようになりました
これは非常に簡単です。
コードの動作バージョンは次のとおりです。
(async function(){
var response = await superagent.get('...')
console.log(response)
})()
Jsはシングルスレッドです。
ブラウザは3つの部分に分けることができます:
1)イベントループ
2)Web API
3)イベントキュー
イベントループは永久に実行されます。つまり、一種の無限ループです。イベントキューは、すべての関数が何らかのイベント(例:クリック)でプッシュされる場所です。これは、キューから1つずつ実行され、この関数を実行して自己準備するイベントループに入れられます。これは、1つの関数の実行は、キュー内の関数がイベントループで実行される前の関数まで開始されないことを意味します。
ここで、2つの関数をキューにプッシュしたとしましょう。1つはサーバーからデータを取得するためのもので、もう1つはそのデータを利用します。最初にserverRequest()関数をキューにプッシュし、次にutiliseData()関数をプッシュしました。serverRequest関数はイベントループに入り、サーバーからデータを取得するのにかかる時間がわからないため、サーバーを呼び出します。このプロセスには時間がかかると予想されるため、イベントループがビジー状態になり、ページがハングします。ここでWeb APIが役割を果たし、イベントループからこの関数を取得し、サーバーを処理してイベントループを解放し、キューから次の関数を実行できるようにします。キュー内の次の関数はutiliseData()で、ループに入りますが、使用可能なデータがないため、無駄と次の関数の実行はキューの終わりまで続きます(これは非同期呼び出しと呼ばれます。つまり、データを取得するまで他のことを行うことができます)
serverRequest()関数のコードにreturnステートメントが含まれていると仮定します。サーバーからデータを取得すると、WebAPIはそれをキューの最後のキューにプッシュします。このデータを利用するための関数がキューに残っていないため、キューの最後にプッシュされるため、そのデータを利用できません。したがって、AsyncCallから何かを返すことはできません。
したがって、これに対する解決策はコールバックまたはpromiseです。
ここでの回答の1つからの画像、コールバックの使用を正しく説明しています... 関数(サーバーから返されたデータを利用する関数)を関数呼び出しサーバーに渡します。
function doAjax(callbackFunc, method, url) {
var xmlHttpReq = new XMLHttpRequest();
xmlHttpReq.open(method, url);
xmlHttpReq.onreadystatechange = function() {
if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) {
callbackFunc(xmlHttpReq.responseText);
}
}
xmlHttpReq.send(null);
}
私のコードでは、それは次のように呼ばれています
function loadMyJson(categoryValue){
if(categoryValue==="veg")
doAjax(print,"GET","http://localhost:3004/vegetables");
else if(categoryValue==="fruits")
doAjax(print,"GET","http://localhost:3004/fruits");
else
console.log("Data not found");
}
このカスタムライブラリ(Promiseを使用して作成)を使用して、リモート呼び出しを行うことができます。
function $http(apiConfig) {
return new Promise(function (resolve, reject) {
var client = new XMLHttpRequest();
client.open(apiConfig.method, apiConfig.url);
client.send();
client.onload = function () {
if (this.status >= 200 && this.status < 300) {
// Performs the function "resolve" when this.status is equal to 2xx.
// Your logic here.
resolve(this.response);
}
else {
// Performs the function "reject" when this.status is different than 2xx.
reject(this.statusText);
}
};
client.onerror = function () {
reject(this.statusText);
};
});
}
簡単な使用例:
$http({
method: 'get',
url: 'google.com'
}).then(function(response) {
console.log(response);
}, function(error) {
console.log(error)
});
別の解決策は、シーケンシャルエグゼキュータnsynjsを介してコードを実行することです。
基礎となる機能が約束されている場合
nsynjsは、すべてのpromiseを順番に評価し、promiseの結果をdata
プロパティに入れます。
function synchronousCode() {
var getURL = function(url) {
return window.fetch(url).data.text().data;
};
var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
console.log('received bytes:',getURL(url).length);
};
nsynjs.run(synchronousCode,{},function(){
console.log('synchronousCode done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
基礎となる機能が約束されていない場合
手順1.コールバックを使用して関数をnsynjs対応ラッパーにラップします(バージョンが約束されている場合は、この手順をスキップできます)。
var ajaxGet = function (ctx,url) {
var res = {};
var ex;
$.ajax(url)
.done(function (data) {
res.data = data;
})
.fail(function(e) {
ex = e;
})
.always(function() {
ctx.resume(ex);
});
return res;
};
ajaxGet.nsynjsHasCallback = true;
ステップ2.同期ロジックを機能させる:
function process() {
console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}
ステップ3.nsynjsを介して同期的に関数を実行します。
nsynjs.run(process,this,function () {
console.log("synchronous function finished");
});
Nsynjsは、すべての演算子と式を段階的に評価し、遅い関数の結果が準備できていない場合に備えて実行を一時停止します。
その他の例はこちら:https://github.com/amaksr/nsynjs/tree/master/examples
ECMAScript 6には、非同期スタイルで簡単にプログラミングできる「ジェネレータ」があります。
function* myGenerator() {
const callback = yield;
let [response] = yield $.ajax("https://stackoverflow.com", {complete: callback});
console.log("response is:", response);
// examples of other things you can do
yield setTimeout(callback, 1000);
console.log("it delayed for 1000ms");
while (response.statusText === "error") {
[response] = yield* anotherGenerator();
}
}
上記のコードを実行するには、次のようにします。
const gen = myGenerator(); // Create generator
gen.next(); // Start it
gen.next((...args) => gen.next([...args])); // Set its callback function
ES6をサポートしていないブラウザーをターゲットにする必要がある場合は、Babelまたはclosure-compilerを介してコードを実行し、ECMAScript5を生成できます。
...args
パターンが複数の引数を持つコールバックに対処できるように、コールバックは配列にラップされ、それらを読み取るときに非構造化されます。たとえば、ノードfsの場合:
const [err, data] = yield fs.readFile(filePath, "utf-8", callback);
非同期リクエストを処理するためのいくつかのアプローチは次のとおりです。
- BrowserPromiseオブジェクト
- Q -JavaScript用のpromiseライブラリ
- A + Promises.js
- jQueryの延期
- XMLHttpRequest API
- コールバックの概念の使用-最初の回答の実装として
例:複数のリクエストを処理するためのjQueryの遅延実装
var App = App || {};
App = {
getDataFromServer: function(){
var self = this,
deferred = $.Deferred(),
requests = [];
requests.push($.getJSON('request/ajax/url/1'));
requests.push($.getJSON('request/ajax/url/2'));
$.when.apply(jQuery, requests).done(function(xhrResponse) {
return deferred.resolve(xhrResponse.result);
});
return deferred;
},
init: function(){
this.getDataFromServer().done(_.bind(function(resp1, resp2) {
// Do the operations which you wanted to do when you
// get a response from Ajax, for example, log response.
}, this));
}
};
App.init();
私たちは、私たちが「時間」と呼ぶ次元に沿って進行しているように見える宇宙にいることに気づきます。私たちは何時かはよくわかりませんが、「過去」、「現在」、「未来」、「前」、「後」など、それについて推論して話すことができる抽象化と語彙を開発しました。
私たちが構築するコンピュータシステムは、ますます重要な側面として時間を持っています。将来起こるように特定のことが設定されています。次に、それらの最初のことが最終的に発生した後に、他のことが発生する必要があります。これが「非同期性」と呼ばれる基本的な概念です。ますますネットワーク化される世界では、非同期の最も一般的なケースは、リモートシステムが何らかの要求に応答するのを待つことです。
例を考えてみましょう。あなたは牛乳配達人に電話して牛乳を注文します。それが来たら、あなたはそれをあなたのコーヒーに入れたいです。まだここにないので、今コーヒーに牛乳を入れることはできません。あなたはそれがあなたのコーヒーに入れる前にそれが来るのを待たなければなりません。つまり、次の機能は機能しません。
var milk = order_milk();
put_in_coffee(milk);
JSは、それが必要であることを知る方法がないので待つためにorder_milk
、それが実行される前に、最後までをput_in_coffee
。言い換えれば、それorder_milk
が非同期であることを知りません-将来のある時まで牛乳をもたらさないものです。JSやその他の宣言型言語は、待たずに次々とステートメントを実行します。
この問題に対する従来のJSのアプローチは、JSが関数を渡すことができるファーストクラスのオブジェクトとしてサポートしているという事実を利用して、関数をパラメーターとして非同期要求に渡すことです。非同期要求は、完了時に呼び出されます。将来のいつかそのタスク。それが「コールバック」アプローチです。次のようになります。
order_milk(put_in_coffee);
order_milk
キックオフし、牛乳を注文し、牛乳が到着したときにのみ、を呼び出しますput_in_coffee
。
このコールバックアプローチの問題は、結果を報告する関数の通常のセマンティクスをreturn
;で汚染することです。代わりに、関数は、パラメーターとして指定されたコールバックを呼び出して結果を報告してはなりません。また、このアプローチは、イベントのより長いシーケンスを処理するときにすぐに扱いにくくなる可能性があります。たとえば、牛乳がコーヒーに入れられるのを待ってから、3番目のステップであるコーヒーを飲むことを実行したいとします。私は次のようなものを書く必要があります:
order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }
ここでput_in_coffee
、牛乳を入れるための牛乳と、牛乳を入れたらdrink_coffee
実行するアクション()の両方を渡します。このようなコードは、書き込み、読み取り、およびデバッグが困難になります。
この場合、質問のコードを次のように書き直すことができます。
var answer;
$.ajax('/foo.json') . done(function(response) {
callback(response.data);
});
function callback(data) {
console.log(data);
}
約束を入力してください
これが「約束」の概念の動機でした。これは、ある種の将来または非同期の結果を表す特定のタイプの値です。これは、すでに発生したこと、将来発生すること、またはまったく発生しない可能性のあることを表すことができます。Promiseには、という名前の単一のメソッドがあり、then
Promiseが表す結果が実現されたときに実行されるアクションを渡します。
牛乳とコーヒーの場合、order_milk
到着する牛乳の約束を返すように設計し、次のようにアクションput_in_coffee
として指定then
します。
order_milk() . then(put_in_coffee)
これの利点の1つは、これらをつなぎ合わせて、将来発生するシーケンス(「連鎖」)を作成できることです。
order_milk() . then(put_in_coffee) . then(drink_coffee)
あなたの特定の問題に約束を適用しましょう。リクエストロジックを関数内にラップし、promiseを返します。
function get_data() {
return $.ajax('/foo.json');
}
実際、私たちが行ったのは、return
への呼び出しに追加されただけ$.ajax
です。これは、jQueryが$.ajax
すでに一種のpromiseのようなものを返すために機能します。(実際には、詳細に立ち入ることなく、実際の約束を返すためにこの呼び出しをラップするか、$.ajax
その代わりに使用することをお勧めします。)ここで、ファイルをロードして終了するのを待ち、それから何かをする、私たちは簡単に言うことができます
get_data() . then(do_something)
例えば、
get_data() .
then(function(data) { console.log(data); });
then
promiseを使用すると、多くの関数がに渡されることになります。そのため、よりコンパクトなES6スタイルの矢印関数を使用すると便利なことがよくあります。
get_data() .
then(data => console.log(data));
async
キーワード
しかし、同期の場合は一方向に、非同期の場合はまったく異なる方法でコードを記述しなければならないことについて、漠然と不満があります。同期の場合、次のように記述します
a();
b();
しかし、a
非同期の場合、私たちが書かなければならない約束があります
a() . then(b);
上記で、「JSには、最初の呼び出しが終了するのを待ってから2番目の呼び出しを実行する必要があることを知る方法がありません」と述べました。JSにそれを伝える方法があったらいいのではないでしょうか。await
「async」関数と呼ばれる特別なタイプの関数内で使用されるキーワードがあることがわかりました。この機能はESの次期バージョンの一部ですが、適切なプリセットがあれば、Babelなどのトランスパイラーですでに利用可能です。これにより、簡単に書くことができます
async function morning_routine() {
var milk = await order_milk();
var coffee = await put_in_coffee(milk);
await drink(coffee);
}
あなたの場合、あなたは次のようなものを書くことができるでしょう
async function foo() {
data = await get_data();
console.log(data);
}
簡単な答え:foo()
メソッドはすぐに戻りますが、関数が戻った後、$ajax()
呼び出しは非同期で実行されます。問題は、非同期呼び出しが戻ったときに取得した結果をどこにどのように保存するかです。
このスレッドでは、いくつかの解決策が示されています。おそらく最も簡単な方法は、オブジェクトをfoo()
メソッドに渡し、非同期呼び出しの完了後にそのオブジェクトのメンバーに結果を格納することです。
function foo(result) {
$.ajax({
url: '...',
success: function(response) {
result.response = response; // Store the async result
}
});
}
var result = { response: null }; // Object to hold the async result
foo(result); // Returns before the async completes
を呼び出しても、foo()
何も役に立たないことに注意してください。ただし、非同期呼び出しの結果はに保存されresult.response
ます。
成功のcallback()
中で関数を使用しますfoo()
。この方法で試してください。シンプルでわかりやすいです。
var lat = "";
var lon = "";
function callback(data) {
lat = data.lat;
lon = data.lon;
}
function getLoc() {
var url = "http://ip-api.com/json"
$.getJSON(url, function(data) {
callback(data);
});
}
getLoc();
Promiseの使用
この質問に対する最も完璧な答えは、を使用することPromise
です。
function ajax(method, url, params) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
resolve(this.responseText);
};
xhr.onerror = reject;
xhr.open(method, url);
xhr.send(params);
});
}
使用法
ajax("GET", "/test", "acrive=1").then(function(result) {
// Code depending on result
})
.catch(function() {
// An error occurred
});
ちょっと待って...!
promiseの使用に問題があります!
なぜ独自のカスタムPromiseを使用する必要があるのですか?
古いブラウザにエラーがあることがわかるまで、私はしばらくこのソリューションを使用していました。
Uncaught ReferenceError: Promise is not defined
そこで、ES3用の独自のPromiseクラスを、定義されていない場合は以下のjsコンパイラに実装することにしました。このコードをメインコードの前に追加するだけで、Promiseを安全に使用できます。
if(typeof Promise === "undefined"){
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Promise = function () {
function Promise(main) {
var _this = this;
_classCallCheck(this, Promise);
this.value = undefined;
this.callbacks = [];
var resolve = function resolve(resolveValue) {
_this.value = resolveValue;
_this.triggerCallbacks();
};
var reject = function reject(rejectValue) {
_this.value = rejectValue;
_this.triggerCallbacks();
};
main(resolve, reject);
}
Promise.prototype.then = function then(cb) {
var _this2 = this;
var next = new Promise(function (resolve) {
_this2.callbacks.push(function (x) {
return resolve(cb(x));
});
});
return next;
};
Promise.prototype.catch = function catch_(cb) {
var _this2 = this;
var next = new Promise(function (reject) {
_this2.callbacks.push(function (x) {
return reject(cb(x));
});
});
return next;
};
Promise.prototype.triggerCallbacks = function triggerCallbacks() {
var _this3 = this;
this.callbacks.forEach(function (cb) {
cb(_this3.value);
});
};
return Promise;
}();
}
質問は:
非同期呼び出しから応答を返すにはどうすればよいですか?
これは次のように解釈できます。
非同期コードを同期的に見えるようにする方法は?
解決策は、コールバックを回避し、Promisesとasync / awaitの組み合わせを使用することです。
Ajaxリクエストの例を挙げたいと思います。
(Javascriptで記述できますが、Pythonで記述し、Transcryptを使用してJavascriptにコンパイルすることを好みます。十分に明確になります。)
最初にJQueryの使用を有効にして、次のように$
利用できるようにしS
ます。
__pragma__ ('alias', 'S', '$')
Promiseを返す関数を定義します。この場合はAjax呼び出しです。
def read(url: str):
deferred = S.Deferred()
S.ajax({'type': "POST", 'url': url, 'data': { },
'success': lambda d: deferred.resolve(d),
'error': lambda e: deferred.reject(e)
})
return deferred.promise()
非同期コードを同期のように使用します。
async def readALot():
try:
result1 = await read("url_1")
result2 = await read("url_2")
except Exception:
console.warn("Reading a lot failed")
もちろん、同期リクエスト、プロミスのような多くのアプローチがありますが、私の経験から、コールバックアプローチを使用する必要があると思います。Javascriptの非同期動作は自然なことです。したがって、コードスニペットは少し異なって書き直すことができます。
function foo() {
var result;
$.ajax({
url: '...',
success: function(response) {
myCallback(response);
}
});
return result;
}
function myCallback(response) {
// Does something.
}
ここですべての応答を読み、私の経験を踏まえて、callback, promise and async/await
JavaScriptでの非同期プログラミングの詳細を再開したいと思い
ます。
1)コールバック:コールバックの基本的な理由は、イベントに応答してコードを実行することです(以下の例を参照)。JavaScriptでは毎回コールバックを使用しています。
const body = document.getElementsByTagName('body')[0];
function callback() {
console.log('Hello');
}
body.addEventListener('click', callback);
ただし、以下の例でネストされたコールバックを多数使用する必要がある場合、コードのリファクタリングは非常にひどいものになります。
asyncCallOne(function callback1() {
asyncCallTwo(function callback2() {
asyncCallThree(function callback3() {
...
})
})
})
2)Promise:構文ES6-Promiseはコールバック地獄の問題を解決します!
const myFirstPromise = new Promise((resolve, reject) => {
// We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
// In this example, we use setTimeout(...) to simulate async code.
// In reality, you will probably be using something like XHR request or an HTML5 API.
setTimeout(() => {
resolve("Success!") // Yay! Everything went well!
}, 250)
})
myFirstPromise
.then((res) => {
return res.json();
})
.then((data) => {
console.log(data);
})
.catch((e) => {
console.log(e);
});
myFirstPromiseは、非同期コードのプロセスを表すPromiseインスタンスです。resolve関数は、Promiseインスタンスが終了したことを通知します。その後、promiseインスタンスで.then()(必要に応じて.thenのチェーン)と.catch()を呼び出すことができます。
then — Runs a callback you pass to it when the promise has fulfilled.
catch — Runs a callback you pass to it when something went wrong.
3)Async / Await:新しい構文ES6-Awaitは基本的にPromiseの砂糖構文です!
非同期関数は、promiseで得られるのと同じ結果を達成するために、より少ないコードを記述できるようにするクリーンで簡潔な構文を提供します。Async / Awaitは同期コードに似ており、同期コードは読み取りと書き込みがはるかに簡単です。Async / Awaitのエラーをキャッチするには、ブロックを使用できますtry...catch
。ここでは、Promise構文の.then()のチェーンを作成する必要はありません。
const getExchangeRate = async () => {
try {
const res = await fetch('https://getExchangeRateData');
const data = await res.json();
console.log(data);
} catch (err) {
console.error(err);
}
}
getExchangeRate();
結論:これらは、JavaScriptでの非同期プログラミングの3つの構文であり、よく理解している必要があります。したがって、可能であれば、非同期コードのリファクタリングには「promise」または「async / await」を使用することをお勧めします(主にXHRリクエストの場合)。
コードをスローするのではなく、JSがコールバックと非同期性を処理する方法を理解するための鍵となる2つの概念があります。(それも一言ですか?)
イベントループと同時実行モデル
知っておく必要のあることが3つあります。待ち行列; イベントループとスタック
大まかに言えば、イベントループはプロジェクトマネージャーのようなもので、実行したい関数を常にリッスンし、キューとスタックの間で通信します。
while (queue.waitForMessage()) {
queue.processNextMessage();
}
何かを実行するためのメッセージを受信すると、それをキューに追加します。キューは、実行を待機しているもののリストです(AJAXリクエストなど)。このように想像してみてください。
1. call foo.com/api/bar using foobarFunc
2. Go perform an infinite loop
... and so on
これらのメッセージの1つが実行されると、キューからメッセージがポップされてスタックが作成されます。スタックは、メッセージ内の命令を実行するためにJSが実行する必要があるすべてのものです。したがって、この例では、呼び出すように指示されていますfoobarFunc
function foobarFunc (var) {
console.log(anotherFunction(var));
}
したがって、foobarFuncが実行する必要があるもの(この場合anotherFunction
)はすべてスタックにプッシュされます。実行された後、忘れられた-イベントループはキュー内の次のものに移動します(またはメッセージをリッスンします)
ここで重要なのは、実行の順序です。あれは
何かが実行されるとき
AJAXを使用して外部パーティに電話をかけるか、非同期コード(たとえば、setTimeout)を実行すると、Javascriptは、続行する前に応答に依存します。
大きな問題は、いつ応答が得られるかということです。答えはわかりません。そのため、イベントループは、そのメッセージが「heyrunme」と言うのを待っています。JSがそのメッセージを同期的に待っていた場合、アプリはフリーズし、最悪の事態になります。したがって、JSは、メッセージがキューに追加されるのを待っている間、キュー内の次のアイテムの実行を続行します。
そのため、非同期機能ではコールバックと呼ばれるものを使用します。文字通り約束のようなものです。私が何かを返すことを約束するように、 jQueryはdeffered.done
deffered.fail
and deffered.always
(とりわけ)と呼ばれる特定のコールバックを使用します。あなたはそれらすべてをここで見ることができます
したがって、実行する必要があるのは、渡されたデータを使用して、ある時点で実行することが約束されている関数を渡すことです。
コールバックはすぐには実行されませんが、後で実行されるのではなく、関数への参照を渡すことが重要です。そう
function foo(bla) {
console.log(bla)
}
そのため、ほとんどの場合(常にではありませんが)合格しfoo
ませんfoo()
うまくいけば、それはある程度意味があるでしょう。このような混乱を招くような事態に遭遇した場合は、少なくともそれを理解するために、ドキュメントを完全に読むことを強くお勧めします。それはあなたをはるかに優れた開発者にするでしょう。