Promise
非同期処理を統一的に扱う方法を定義したオブジェクト
型は Promise<string | undefined>
みたいな感じ
非同期処理の成功時、失敗時の処理を明示的に書くことができる
非同期処理を並行、直列に実行できる
Promiseは3つの状態を持ち、それに伴って値を保持する
何が嬉しいか?
コールバックによるネストが深くなる問題を解決
タイミング不定問題を解決
インターフェースが制限されたことでコールバックの指定の仕方がわかりやすくなった
new Promise
、 then
、 catch
の基本について
Promise.all
など
参考
使用の観点でわかりやすい
概念の理解としてわかりやすい
Promiseを再実装しているので細かいところも追える
途中まで読んだ
途中まで読んだ
tstype 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
された時
tsnew Promise<number>((resolve) => {
throw new Error("err");
}).catch((err) => {
console.error(`Caught by .catch ${err}`);
});
rejected
が呼ばれた時
tstype 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()
を伝達しているが別に文字列でもなんでも良い

throwしないで、rejectしよう
実行のタイミング
Promiseを生成したタイミングで内部の処理は実行される
ただし、コールバック関数は実行されない
then()
や catch()
が呼ばれたタイミングでコールバック関数が実行される
例
こんな関数を定義する
tsconst 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を呼ばないと、コールバック関数は実行されない
result asyncFunction();
$ ts-node src/index.ts
1
2
3
4
当たり前だが、こう書いても全く同じ挙動になるよ
tsconst r = asyncFunction(); // 1,2,3,4は実行される
thenを呼ぶとコールバック関数が実行される
result asyncFunction().then(v => console.log(v));
$ ts-node src/index.ts
1
2
3
4
Promise Hello world
resolveの引数を複数にしても捨てられる?
then節の関数を複数にしても型エラー、実行時エラーになる
tsnew Promise((resolve: (arg: number, str: string, n: boolean) => void) => {
setTimeout(() => {
resolve(10, "hoge", true); // ここの"hoge", trueはどこに行く?
}, 16);
});
thenなどの結果を変数に束縛できない?
tsconst 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>
になっている
むり?そもそもそういうのが欲しくなるニーズがない?
cansel
自作
Promiseの拡張ライブラリがある
then節のチェーンは、「普通は」毎回新しいPromiseを返すような書き方をする?
普通の値も返すこともある?
それぞれのユースケースを知りたい
A. 実用的な例
fetchAPIはPromiseを返し、その返り値に含まれる
.json()
もまたPromiseを返す
tsfetch("http://example.com/movies.json")
.then(response => response.json())
.then(json => console.log(json))
.catch(err => console.log(err));
ちなみにこのコードはnodeではなくブラウザ上でないと動かない
tsconst 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は入れ子にしても二重になるわけではない
jsconsole.log(
Promise.resolve(
Promise.resolve(
Promise.resolve(1)
)
)
) //=> Promise { 1 }
deepdive
PromiseによるJavaScript非同期処理レシピ集