Promise
非同期処理を統一的に扱う方法を定義したオブジェクト
型はPromise<string | undefined>みたいな感じ
非同期処理の成功時、失敗時の処理を明示的に書くことができる
非同期処理を並行、直列に実行できる
Promiseは3つの状態を持ち、それに伴って値を保持する
何が嬉しいか?
コールバックによるネストが深くなる問題を解決
タイミング不定問題を解決
インターフェースが制限されたことでコールバックの指定の仕方がわかりやすくなった
new Promise、then、catchの基本について
Promise.allなど
参考
使用の観点でわかりやすい
概念の理解としてわかりやすい
Promiseを再実装しているので細かいところも追える
途中まで読んだ
途中まで読んだ
code:ts
type Constructor<T> = (
executor: (
resolve: (value?: unknown) => void,
reject: (reason?: any) => void
) => void
) => Promise<T>;
Promiseオブジェクトを生成
インスタンス生成のイメージ
2重の高階関数になっている
new Promise(fn)のfnは関数
fnである(resolve, reject)=> voidのresolveとrejectも関数
new Promise((t=>void, e=>void) => void)
fnの引数となる関数
resolve(v)
処理が成功したことを表す
成功した結果としてvを返す
reject(e)
処理が失敗したことを表す
err情報として、eを返す
.finally()
成功時も失敗時も呼ばれる
throwされた時
code:ts
new Promise<number>((resolve) => {
throw new Error("err");
}).catch((err) => {
console.error(Caught by .catch ${err});
});
rejectedが呼ばれた時
code:ts
type User = { id: number; username: string };
const authorized = false;
function getUserById(id: number) {
return new Promise<User>((resolve, reject) => {
if (!authorized) {
reject(new Error("Unauthorized access to the user data"));
}
resolve({ id, username: "admin" });
});
}
getUserById(10)
.then((user) => console.log(user.username))
.catch((err) => console.log(Caught by .catch ${err}));
上2つの例では両方ともnew Error()を伝達しているが別に文字列でもなんでも良いmrsekut.icon
throwしないで、rejectしよう
実行のタイミング
Promiseを生成したタイミングで内部の処理は実行される
ただし、コールバック関数は実行されない
then()やcatch()が呼ばれたタイミングでコールバック関数が実行される
例
こんな関数を定義する
code:ts
const asyncFunction = () => {
console.log("1");
return new Promise((resolve, reject) => {
console.log("2");
setTimeout(() => {
console.log("3");
resolve("Promise Hello world");
console.log("4");
}, 16);
});
};
thenを呼ばないと、コールバック関数は実行されない
code:result
asyncFunction();
$ ts-node src/index.ts
1
2
3
4
当たり前だが、こう書いても全く同じ挙動になるよ
code:ts
const r = asyncFunction(); // 1,2,3,4は実行される
thenを呼ぶとコールバック関数が実行される
code:result
asyncFunction().then(v => console.log(v));
$ ts-node src/index.ts
1
2
3
4
Promise Hello world
resolveの引数を複数にしても捨てられる?
then節の関数を複数にしても型エラー、実行時エラーになる
code:ts
new Promise((resolve: (arg: number, str: string, n: boolean) => void) => {
setTimeout(() => {
resolve(10, "hoge", true); // ここの"hoge", trueはどこに行く?
}, 16);
});
thenなどの結果を変数に束縛できない?
code:ts
const asyncFunction = () =>
new Promise<number>(resolve => {
setTimeout(() => {
resolve(10);
}, 16);
});
const str = asyncFunction()
.then(value => value + 20)
.then(value => value + 30)
.catch(error => {
console.log(error);
});
console.log(str) // 60...というふうにしたい。
↑では、strの型はPromise<number|undefiend>になっている
むり?そもそもそういうのが欲しくなるニーズがない?
https://www.youtube.com/watch?v=AwyoVjVXnLk
cansel
自作
Promiseの拡張ライブラリがある
then節のチェーンは、「普通は」毎回新しいPromiseを返すような書き方をする?
普通の値も返すこともある?
それぞれのユースケースを知りたい
A. 実用的な例
fetchAPIはPromiseを返し、その返り値に含まれる.json()もまたPromiseを返す code:ts
.then(response => response.json())
.then(json => console.log(json))
.catch(err => console.log(err));
ちなみにこのコードはnodeではなくブラウザ上でないと動かない
code:ts
const doubleE = (data: number): Promise<number> =>
new Promise((f, e) => {
setTimeout(() => {
if (Math.random() < 0.3) {
e(new Error("ERROR!"));
} else {
f(data * 2); // こことかを、return f(data*2)にしたときとの違い
}
}, Math.random() * 1000);
});
Promiseは入れ子にしても二重になるわけではない
code:js
console.log(
Promise.resolve(
Promise.resolve(
Promise.resolve(1)
)
)
) //=> Promise { 1 }
deepdive
PromiseによるJavaScript非同期処理レシピ集