一つはjavascript promiseの例を使用します.
68484 ワード
複雑な非同期コードは簡単になりました.
OKです.今は実際のコードを書きます.私たちが望むなら:は、ローディング指示アイコン を表示する.は小説のJSONをロードして、小説名と各章の内容のURLを含みます. ページに小説名を記入する すべての章の本文を読み込みます. ページに章本文を追加する ローディング停止指示 この過程で何か間違いがあったら、ユーザーに知らせて、ロード指示を止めます.そうしないと、目がくらくらしたり、インターフェースを壊したりします.
もちろん、JavaScriptを使ってこのように煩雑に文章を表示することはできません.直接HTMLを出力するのはずっと速いですが、このプロセスはとても典型的なAPI要求モードです.
まず、ネットワークからデータをロードするステップができます.
PromiseをXMLtpRequestに使用します.
後方互換性を維持できれば、既存のAPIはPromiseをサポートするために更新され、
チェーンコール
「then」の物語はまだ終わっていません.これらの「then」を直列に修正したり追加したりして、もっと非同期的に操作してもいいです.
値の処理
結果を修正して新しい値に戻ります.
キューの非同期操作
「then」を直列にして順番に非同期操作を行うこともできます.
「then」のコールバック関数から戻ってきたら、ここはちょっと魔法です.値を返したら、次の「then」へのフィードバックがあります.もしあなたが「クラスPromise」の対象に戻ったら、次の「then」はこのPromiseが明確に終了するのを待っています.たとえば:
エラー処理
前に見たように、「then」は二つのパラメータを受け入れ、一つの処理が成功し、一つの処理が失敗しました.
緑の線は肯定的なPromiseの流れで、赤い糸は否定的なPromiseの流れです.
JavaScript異常とPromise
Promiseの否定応答はPromise.reject()によってトリガされてもよいし、コンストラクタのリピートにおいて投げられたエラーによってトリガされてもよい.
「then」コールバックで投げたエラーも同じです.
私たちの物語と章に戻ります.
JavaScriptのtry/catchと同じように、エラーを捕まえたら、次のコードは継続して実行します.計画通りにロードインジケータを停止します.上のコードは次の段の非ブロック非同期版です.
並行とシリアル――魚と熊の手のひらを兼ねています.
異歩の思考方式は直感に合わないです.もしスタートが難しいと感じたら、まず同期の方法を書いてみます.これのようです.
シーケンスを作成
章URL配列をPromiseのシーケンスに変換しますか?
もう一つの対応があります.
私たちは使えます
上のコードをまとめます.
ブラウザは複数のファイルを同時に読み込むのが得意です.このように、章を次々にダウンロードする方法は非常に効率的です.私達は同時にすべての章をダウンロードして、全部完成したら一回で解決したいです.ちょうどこのようなAPIがあります.
しかし、それは空間を向上させることです.第一章の内容をロードし終わったら、すぐにページに記入してください.そうすると、他のロードタスクがまだ完成していないうちに、ユーザは読み始めることができます.第三章に着いたら、私たちは黙っています.第二章も着いたら、第二章と第三章の内容をページに記入します.これを類推します.
このような効果を達成するために、私達は同時にすべての章の内容を要求し、次の順序でページに記入します.
この小例では、各章の読み込みがほぼ同時に行われ、章ごとに表示されるポリシーは、章の内容が多い場合にはより優勢になります.
上のコードを使えば Node.jsスタイルのコールバックやイベントメカニズムが実現すれば、コードの量は大体倍になります.もっと重要なのは可読性もこの例に及ばないです.しかし、Promiseの凄さはこれだけではなく、他のES 6の新機能と組み合わせてさらに効率的に…
OKです.今は実際のコードを書きます.私たちが望むなら:
もちろん、JavaScriptを使ってこのように煩雑に文章を表示することはできません.直接HTMLを出力するのはずっと速いですが、このプロセスはとても典型的なAPI要求モードです.
まず、ネットワークからデータをロードするステップができます.
PromiseをXMLtpRequestに使用します.
後方互換性を維持できれば、既存のAPIはPromiseをサポートするために更新され、
XMLHttpRequest
対象を重点的に考える一つです.でも、とりあえずGETリクエストを書きます.function get(url) {
// Promise
return new Promise(function(resolve, reject) {
// XHR
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function() {
// 404
//
if (req.status == 200) {
// , Promise
resolve(req.response);
}
else {
// Promise
// ( Error )
reject(Error(req.statusText));
}
};
//
req.onerror = function() {
reject(Error("Network Error"));
};
//
req.send();
});
}
今呼び出せます.get('story.json').then(function(response) {
console.log("Success!", response);
}, function(error) {
console.error("Failed!", error);
});
ここをクリックしてコードを確認します.今は手で打つ必要がありません. XMLHttpRequest
直接HTTPの要求を始めることができて、このように感じるのはずっと良くなって、一回のこの狂うラクダの峰の命名のを少し見ることができます. XMLHttpRequest
私はもっと楽しいです.チェーンコール
「then」の物語はまだ終わっていません.これらの「then」を直列に修正したり追加したりして、もっと非同期的に操作してもいいです.
値の処理
結果を修正して新しい値に戻ります.
var promise = new Promise(function(resolve, reject) {
resolve(1);
});
promise.then(function(val) {
console.log(val); // 1
return val + 2;
}).then(function(val) {
console.log(val); // 3
});
前のコードに戻る:get('story.json').then(function(response) {
console.log("Success!", response);
});
受信した応答は純粋なテキストのJSONで、get関数の設定を変更できます. responseType
はJSONのためにサーバ応答フォーマットを指定します.Promiseの世界でこの問題を解決することもできます.get('story.json').then(function(response) {
return JSON.parse(response);
}).then(function(response) {
console.log("Yey JSON!", response);
});
からには JSON.parse
一つのパラメータだけを受信して、変換後の結果を返します.get('story.json').then(JSON.parse).then(function(response) {
console.log("Yey JSON!", response);
});
コード実行ページをクリックして、コントロールを開いて出力結果を確認します. 実は、getJSON
をお願いします. 関数の書き方はとても簡単です.function getJSON(url) {
return get(url).then(JSON.parse);
}
getJSON
JSONを取得して解析するPromiseを返します.キューの非同期操作
「then」を直列にして順番に非同期操作を行うこともできます.
「then」のコールバック関数から戻ってきたら、ここはちょっと魔法です.値を返したら、次の「then」へのフィードバックがあります.もしあなたが「クラスPromise」の対象に戻ったら、次の「then」はこのPromiseが明確に終了するのを待っています.たとえば:
getJSON('story.json').then(function(story) {
return getJSON(story.chapterUrls[0]);
}).then(function(chapter1) {
console.log("Got chapter 1!", chapter1);
});
ここで私たちは「story.json」に対する非同期の要求を開始し、より多くのURLを返してください.そして、その中の最初の一つをお願いします.Promiseは初めて事件の折り返しに優越性を示しました.章の内容を把握する独立した関数を書くこともできます.var storyPromise;
function getChapter(i) {
storyPromise = storyPromise || getJSON('story.json');
return storyPromise.then(function(story) {
return getJSON(story.chapterUrls[i]);
})
}
// :
getChapter(0).then(function(chapter) {
console.log(chapter);
return getChapter(1);
}).then(function(chapter) {
console.log(chapter);
});
私たちは最初にstory.jsonをロードしませんでした.初めてです. getChapter
、その後は毎回getChapter
. ロード済みのstory Promiseを再利用することができますので、story.jsonは一回だけお願いします.Promiseは素晴らしいですエラー処理
前に見たように、「then」は二つのパラメータを受け入れ、一つの処理が成功し、一つの処理が失敗しました.
get('story.json').then(function(response) {
console.log("Success!", response);
}, function(error) {
console.log("Failed!", error);
});
「catch」も使えます.get('story.json').then(function(response) {
console.log("Success!", response);
}).catch(function(error) {
console.log("Failed!", error);
});
ここのcatchは特別なところがありません. then(undefined, func)
の文法糖衣はもっと直観的です.上の二つのコードの行動は同じだけでなく、後者は以下の通りです.get('story.json').then(function(response) {
console.log("Success!", response);
}).then(undefined, function(error) {
console.log("Failed!", error);
});
大きな違いはないが、意味は非常に大きい.Promiseが否定されると、その後最初に否定反転を配置したthen(またはcatch)にジャンプします.に対する then(func1, func2)
必ず呼び出します. func1
または func2
一つは、二つとも呼び出しません.に対する then(func1).catch(func2)
このように func1
否定を返すと func2
また、彼らはチェーン呼び出しの独立した2つのステップで呼び出されます.下のコードを見てください.asyncThing1().then(function() {
return asyncThing2();
}).then(function() {
return asyncThing3();
}).catch(function(err) {
return asyncRecovery1();
}).then(function() {
return asyncThing4();
}, function(err) {
return asyncRecovery2();
}).catch(function(err) {
console.log("Don't worry about it");
}).then(function() {
console.log("All done!");
});
この流れはJavaScriptのtry/catchのように非常によく似ています.「try」コードブロックで発生したエラーはすぐに「catch」コードブロックにジャンプします.これは上のコードのフローチャートです.緑の線は肯定的なPromiseの流れで、赤い糸は否定的なPromiseの流れです.
JavaScript異常とPromise
Promiseの否定応答はPromise.reject()によってトリガされてもよいし、コンストラクタのリピートにおいて投げられたエラーによってトリガされてもよい.
var jsonPromise = new Promise(function(resolve, reject) {
// JSON.parse
// :
resolve(JSON.parse("This ain't JSON"));
});
jsonPromise.then(function(data) {
// :
console.log("It worked!", data);
}).catch(function(err) {
// :
console.log("It failed!", err);
});
これは、すべてのPromise関連の操作をその構造関数のフィードバックにおいて行うことができ、このように何が起こっても捕まり、Promise否定をトリガすることができるという意味です.「then」コールバックで投げたエラーも同じです.
get('/').then(JSON.parse).then(function() {
// This never happens, '/' is an HTML page, not JSON
// so JSON.parse throws
console.log("It worked!", data);
}).catch(function(err) {
// Instead, this happens:
console.log("It failed!", err);
});
実践上のエラー処理私たちの物語と章に戻ります.
catch
を選択します.getJSON('story.json').then(function(story) {
return getJSON(story.chapterUrls[0]);
}).then(function(chapter1) {
addHtmlToPage(chapter1.html);
}).catch(function() {
addTextToPage("Failed to show chapter");
}).then(function() {
document.querySelector('.spinner').style.display = 'none';
});
お願いがあれば story.chapterUrls[0]
失敗(http 500またはユーザーがドロップするなど)しました.それはスキップした後、成功に対するすべてのコールバックを含みます. getJSON
応答をJSONのコールバックとして解析し、ここで最初の内容をページに追加したコールバックです.JavaScriptの実行はcatchフィードバックに入ります.結果として、前の章の要求ミスで、ページには「Failed to show chapter」と表示されます.JavaScriptのtry/catchと同じように、エラーを捕まえたら、次のコードは継続して実行します.計画通りにロードインジケータを停止します.上のコードは次の段の非ブロック非同期版です.
try {
var story = getJSONSync('story.json');
var chapter1 = getJSONSync(story.chapterUrls[0]);
addHtmlToPage(chapter1.html);
}
catch (e) {
addTextToPage("Failed to show chapter");
}
document.querySelector('.spinner').style.display = 'none';
異常をキャプチャーして記録出力をするだけで、ユーザーインターフェースでエラーをフィードバックするつもりはないなら、Errを捨てればいいです.このステップはErrに置くことができます. getJSON
中:function getJSON(url) {
return get(url).then(JSON.parse).catch(function(err) {
console.log("getJSON failed for", url, err);
throw err;
});
}
今は第一章をクリアしました.これから全章をクリアします.並行とシリアル――魚と熊の手のひらを兼ねています.
異歩の思考方式は直感に合わないです.もしスタートが難しいと感じたら、まず同期の方法を書いてみます.これのようです.
try {
var story = getJSONSync('story.json');
addHtmlToPage(story.heading);
story.chapterUrls.forEach(function(chapterUrl) {
var chapter = getJSONSync(chapterUrl);
addHtmlToPage(chapter.html);
});
addTextToPage("All done");
}
catch (err) {
addTextToPage("Argh, broken: " + err.message);
}
document.querySelector('.spinner').style.display = 'none';
これは完全に正常に実行されます.しかし、それは同期されています.コンテンツを読み込む時、ブラウザ全体に引っかかります.それを非同期的に働かせるためには、thenを使ってそれらを次々に連結します.getJSON('story.json').then(function(story) {
addHtmlToPage(story.heading);
// TODO: story.chapterUrls url
}).then(function() {
// !
addTextToPage("All done");
}).catch(function(err) {
//
addTextToPage("Argh, broken: " + err.message);
}).then(function() {
// spinner
document.querySelector('.spinner').style.display = 'none';
});
では、私たちはどうやって章のURLを巡回して、順次要求しますか?これはいけません.story.chapterUrls.forEach(function(chapterUrl) {
// Fetch chapter
getJSON(chapterUrl).then(function(chapter) {
// and add it to the page
addHtmlToPage(chapter.html);
});
});
「forEach」は非同期操作に対するサポートがないので、私たちのストーリーパートはそれらのロードが完了した順に表示されます.基本的には「低俗小説」はこのように書かれています.低俗小説を書かないので、修正します.シーケンスを作成
章URL配列をPromiseのシーケンスに変換しますか?
then
:// Promise
var sequence = Promise.resolve();
// url
story.chapterUrls.forEach(function(chapterUrl) {
// sequence
sequence = sequence.then(function() {
return getJSON(chapterUrl);
}).then(function(chapter) {
addHtmlToPage(chapter.html);
});
});
これは私たちが初めて使うものです. Promise.resolve
は、あなたが送ったどの値によってもPromiseに戻ります.もしあなたがこのクラスのPromiseオブジェクトに伝えたら then
方法)は、同一の肯定/否定反転を伴うPromiseを生成します.基本的にはクローンです.Promise.resolve('Hello')
のような他の値が伝えられたら、この値で完成されたPromiseを作成します.何の値も伝えないなら、undefinedを完成させます.もう一つの対応があります.
Promise.reject(val)
は、あなたが入ってきたパラメータ(またはundefined)を否定結果としてPromiseを作成します.私たちは使えます
array.reduce
上のコードを簡単にしてください.// url
story.chapterUrls.reduce(function(sequence, chapterUrl) {
// sequence
return sequence.then(function() {
return getJSON(chapterUrl);
}).then(function(chapter) {
addHtmlToPage(chapter.html);
});
}, Promise.resolve());
前の例と同じ機能ですが、明示的な声明は必要ありません. sequence
変数reduce
順番に各配列要素に適用されます.第1ラウンドの「sequence」は Promise.resolve()
、その後の呼び出しで「sequence」は前回の関数で実行された結果です.array.reduce
一組の値を一つの値にまとめるには非常に適しています.今のPromiseの使い方です.上のコードをまとめます.
getJSON('story.json').then(function(story) {
addHtmlToPage(story.heading);
return story.chapterUrls.reduce(function(sequence, chapterUrl) {
// Promise ……
return sequence.then(function() {
// ……
return getJSON(chapterUrl);
}).then(function(chapter) {
//
addHtmlToPage(chapter.html);
});
}, Promise.resolve());
}).then(function() {
// !
addTextToPage("All done");
}).catch(function(err) {
//
addTextToPage("Argh, broken: " + err.message);
}).then(function() {
// spinner
document.querySelector('.spinner').style.display = 'none';
});
コードの実行例を見ると、前の同期コードは完全に非同期バージョンに改造されています.私たちはもっと進むことができます.現在のページに読み込む効果はこうです.ブラウザは複数のファイルを同時に読み込むのが得意です.このように、章を次々にダウンロードする方法は非常に効率的です.私達は同時にすべての章をダウンロードして、全部完成したら一回で解決したいです.ちょうどこのようなAPIがあります.
Promise.all(arrayOfPromises).then(function(arrayOfResults) {
//...
});
Promise.all
一つのPromise配列をパラメータとして受け入れて、すべてのPromiseが完成したら完成するPromiseを作成します.その完成結果は一つの配列で、先に伝えたすべてのPromiseの完成結果を含んでいます.順序はそれらを導入する配列順序と一致します.getJSON('story.json').then(function(story) {
addHtmlToPage(story.heading);
// Promise
return Promise.all(
// URL Promise
story.chapterUrls.map(getJSON)
);
}).then(function(chapters) {
// JSON, ……
chapters.forEach(function(chapter) {
// ……
addHtmlToPage(chapter.html);
});
addTextToPage("All done");
}).catch(function(err) {
//
addTextToPage("Argh, broken: " + err.message);
}).then(function() {
document.querySelector('.spinner').style.display = 'none';
});
接続状況に応じて、コードの改良は、順序付けよりも数秒速くなります.コードの行数も少ないです.章の読み込みが完了する順序は不明ですが、ページに表示される順序は正確です.しかし、それは空間を向上させることです.第一章の内容をロードし終わったら、すぐにページに記入してください.そうすると、他のロードタスクがまだ完成していないうちに、ユーザは読み始めることができます.第三章に着いたら、私たちは黙っています.第二章も着いたら、第二章と第三章の内容をページに記入します.これを類推します.
このような効果を達成するために、私達は同時にすべての章の内容を要求し、次の順序でページに記入します.
getJSON('story.json').then(function(story) {
addHtmlToPage(story.heading);
// URL Promise
//
return story.chapterUrls.map(getJSON)
.reduce(function(sequence, chapterPromise) {
// reduce Promise
//
return sequence.then(function() {
// sequence
return chapterPromise;
}).then(function(chapter) {
addHtmlToPage(chapter.html);
});
}, Promise.resolve());
}).then(function() {
addTextToPage("All done");
}).catch(function(err) {
//
addTextToPage("Argh, broken: " + err.message);
}).then(function() {
document.querySelector('.spinner').style.display = 'none';
});
ははは(例を調べて)、魚と熊の掌は兼ねます!すべてのコンテンツをロードする時間は変更されていませんが、ユーザーはより早く第1章を見ることができます.この小例では、各章の読み込みがほぼ同時に行われ、章ごとに表示されるポリシーは、章の内容が多い場合にはより優勢になります.
上のコードを使えば Node.jsスタイルのコールバックやイベントメカニズムが実現すれば、コードの量は大体倍になります.もっと重要なのは可読性もこの例に及ばないです.しかし、Promiseの凄さはこれだけではなく、他のES 6の新機能と組み合わせてさらに効率的に…