本記事は一人アドベントカレンダー企画の一つです。
30代未経験エンジニアが25日後にJavaScriptをマスターするカレンダー
JAVASCRIPT.INFOを元にJavaScriptを勉強していき、そこで学んだ知識をアウトプットしていきます。
25日でJAVASCRIPT.INFOをやりきり、未経験エンジニアがJavaScriptをマスターする過程を投稿していきます。
11.5 Promise API
知らない単語
- フェッチとは
- 機器やプログラムなどが特定の場所からデータなどを読み出す動作の事
学んだこと
Promise クラスには 6 つの静的メソッドがある
- Promise.all
複数の promise を実行し、すべて完了してから処理を開始する場合、
Promise.allを使用する
let promise = Promise.all(iterable);
Promise.all は iterableを取り、新しい promise を返す
新しい promise は、他のpromise がすべて解決され、処理結果が配列に入ると解決される
例えば、下記は Promise.all が 3 秒後に解決され、その後結果は配列 [1, 2, 3] になる
Promise.all([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 3000)), // 1
new Promise((resolve, reject) => setTimeout(() => resolve(2), 2000)), // 2
new Promise((resolve, reject) => setTimeout(() => resolve(3), 1000)) // 3
]).then(alert); // 1,2,3 promise が準備できた時: 各 promise は配列の中身に寄与する
- Promise.allSettled
Promise.all は promise が reject されると、全体を reject する
これは 結果がtrue,false のような2択のケースに適切である
Promise.allSettled は結果に関わらず全ての promise が解決するまで待つ
成功した場合の返り値 {status:"fulfilled", value:result}
エラーの場合の返り値 {status:"rejected", reason:error}
となる
- Polyfill
ポリフィルは、その機能をネイティブにサポートしていないWebブラウザーに機能を実装するコードである
ブラウザでは、Promise.allSettled をサポートしていないが、簡単に polyfill できる
if (!Promise.allSettled) {
const rejectHandler = reason => ({ status: 'rejected', reason });
const resolveHandler = value => ({ status: 'fulfilled', value });
Promise.allSettled = function (promises) {
const convertedPromises = promises.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));
return Promise.all(convertedPromises);
};
}
このコードで、promises.map は入力値を取り、p => Promise.resolve(p) でそれ promise に変換し、.then ハンドラに追加する
その結果 value を {status:'fulfilled', value} に、エラー reason は {status:'rejected', reason} に変換する
これで、いくつかが reject されても全ての promise の結果を得て、 Promise.allSettled が使用できる
- Promise.race
Promise.raceはPromise.all と似ているが、すべてが完了するのを待つのではなく、最初の結果のみを待つ
Promise.race([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1
ここでは結果は 1 になる
最初の結果が Promise.race 全体の結果になるので、最初に確定した promise が 優先され、それ以外は無視される
- Promise.any
最初の履行した promise のみを待ち、その結果を得る
指定された promise がすべて reject された場合、返却された promise は AggregateError で reject される
Promise.raceと似ていて、例えば最初の promise がreject されると、2つ目の promise が結果になり、他の結果はすべて無視される
- Promise.resolve/reject
最近のコードでは、Promise.resolve と Promise.reject メソッドはほとんど使われない
Promise.resolve(value)は与えられた値で promise を解決する
Promise.reject(error)は与えられたエラーで promise を拒否する
感想
Promiseの6つのメソッドそれぞれの役割は似ているものもあるので、間違えないように理解して的確に使用していきたい。
11.6 Promisification
知らない単語
- ラッパーとは
- ソフトウェアやプログラム部品などが提供するクラスや関数、データ型などを、本来とは異なる環境や方法で利用できるようにしたもの
学んだこと
Promisification は単純な変換を表す用語である
コールバックを受け付ける関数から、Promise を返す関数へ変換する
例えば、loadScript(src, callback)では、
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(script);
}
// loadScript('path/script.js', (err, script) => {...})
指定された src のスクリプトを読み込み、エラーの場合は callback(err), 読み込みに成功した場合には callback(null, script) を呼び出す
Promise 化すると
let loadScriptPromise = function(src) {
return new Promise((resolve, reject) => {
loadScript(src, (err, script) => {
if (err) reject(err)
else resolve(script);
});
})
}
// loadScriptPromise('path/script.js').then(...)
このようになる
結果を Promise の resolve/reject に変換し、呼び出す
感想
Promise化する方法と通常の2種類あるので、コールバックと promise どちらが良いか、場合によって使い分けていきたい。
11.7 Microtasks
知らない単語
- ECMA 標準とは
- 情報・通信技術(ICT)関連の規格を策定する国際的な標準化団体の一つ、主にコンピュータ関連や通信・ネットワーク関連の技術標準を策定している
学んだこと
.then/.catch/.finally は常に非同期である
なので、.then/catch/finally のハンドラは常にコードが終了した後に呼び出される
なにかの処理で .then/catch/finally の後に実行されることを確定させなければいけない場合、Promise チェーンを使い .then で呼び出す必要がある
- Microtasks キュー
キューは先入れ先出しなので、先にキューに入れられたタスクが最初に実行される
タスクのは他に実行されているものが無い時に開始される
要はPromise が準備できると、 .then/catch/finally ハンドラはキューに入れられ、コードがないときにキューからタスクを取り、実行する
感想
Promiseでは一貫して、.then/catch/finally などのハンドラがいつ実行されるかが、とても重要な事であると思う。
11.8 Async/await
知らない単語
- 無し
学んだこと
“async/await” はpromise を使いやすくする
- Async 関数
async キーワードからはこのように関数の前に置ける
async function f() {
return 1;
}
asyncは1つの単純なことを意味する
関数は常に promise を返し、コードの中に returnがある場合、JavaScript は自動でpromise にラップする
- Await
// async 関数の中でのみ動作する
let value = await promise;
await は promise が確定して、結果を返すまで、JavaScript を待機させる
そしてそれがエラーなら、 throw error が呼び出されたのと同じ例外が生成され、
それ以外の場合は、結果を返し、値に割り当てる
- エラー処理
promise が正常に解決すると、await promise は結果を返すが、rejectの場合はエラーをスローする
async function f() {
await Promise.reject(new Error("Whoops!"));
}
上記のコードは下記と同じである
async function f() {
throw new Error("Whoops!");
}
promise を拒否するまでに時間がかかる
感想
async、await と一緒に .then、catch を書いたりする事もあるのかはわからないが、promiseに関する事なので重要だと思うので、自分で調べて使い方を理解したい。
最後に
Promiseは役割や派生のメソッドも多いので、順を追ってきちんと理解していなければ使えないと思う。
どのような場面でどのメソッドを使ってどのような処理になるのかを整理して覚えておく。