Promise
非同期処理を統一的に扱う方法を定義したオブジェクト
型はPromise<string | undefined>みたいな感じ
非同期処理の成功時、失敗時の処理を明示的に書くことができる
非同期処理を並行、直列に実行できる
起源はE言語のこれらしい
Promiseの状態から考えると理解しやすい
Promiseは3つの状態を持ち、それに伴って値を保持する
pendingから
Promise.resolveすることでfulfilledになる
このときPromiseは、Promise.resolveに渡した値を保持する
この値を取得するためには、Promise.thenを呼ぶ
Promise.rejectすることでrejectedになる
このときPromiseは、Promise.rejectに渡した値を保持する
この値を取得するためには、Promise.catchを呼ぶ
何が嬉しいか?
コールバックによるネストが深くなる問題を解決
タイミング不定問題を解決
インターフェースが制限されたことでコールバックの指定の仕方がわかりやすくなった
Promiseの基本の基本を段階的に理解する
new Promise、then、catchの基本について
Promiseで並列に非同期処理を行う
Promise.allなど
Promise.all()
Promise.race()
Promise.any()
Promise.allSettled()
参考
とほほのPromise入門
使用の観点でわかりやすい
JavaScriptの非同期処理を理解する その2 〜Promise編〜 | さくらのナレッジ
概念の理解としてわかりやすい
Promiseを再実装しているので細かいところも追える
#WIP
Promiseを使う - JavaScript | MDN
途中まで読んだ
JavaScript Promiseの本
途中まで読んだ
https://zenn.dev/estra/books/js-async-promise-chain-event-loop
new Promise(fn)
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()
成功時も失敗時も呼ばれる
Promise内における失敗とは
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
https://azu.github.io/promises-book/#not-throw-use-reject
throwしないで、rejectしよう
https://azu.github.io/promises-book/#ch2-promise-finally
実行のタイミング
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
https://qiita.com/martinheidegger/items/6e8275d2de88174bc7e6
https://zenn.dev/estra/articles/js-async-programming-roadmap
https://zenn.dev/mizchi/articles/understanding-promise-by-ts-eventloop
https://qiita.com/suin/items/b9d00dff380486338ecd
自作
Promiseの拡張ライブラリがある
Promise Q
bluebird
https://qiita.com/kamijin_fanta/items/21a061a00575f0a357c6#promise
#わかった
then節のチェーンは、「普通は」毎回新しいPromiseを返すような書き方をする?
普通の値も返すこともある?
それぞれのユースケースを知りたい
Promiseの基本の基本を段階的に理解する#5e174e6e1982700000098efb
A. 実用的な例
fetchAPIはPromiseを返し、その返り値に含まれる.json()もまたPromiseを返す
code:ts
fetch("http://example.com/movies.json")
.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 }
Futureパターン
https://qiita.com/uhyo/items/a9f6e70f43287cc0f52e
Unhandled Rejection
https://zenn.dev/uhyo/articles/unhandled-rejection-understanding
deepdive
https://typescript-jp.gitbook.io/deep-dive/future-javascript/promise
https://qiita.com/ysk_1031/items/888a84cb259cec4e0625
https://sbfl.net/blog/2019/11/04/promise-cookbook/
PromiseによるJavaScript非同期処理レシピ集
https://speakerdeck.com/ajido/callback-to-promise-and-beyond
https://postd.cc/how-do-promises-work/
https://docs.scala-lang.org/ja/overviews/core/futures.html
https://github.com/bikeshaving/crank
https://nymphium.github.io/2020/05/03/nikkan-promise1.html
https://zenn.dev/qnighy/articles/0aa6ec47248d80
https://lydiahallie.framer.website/blog/promise-execution