Go to list of users who liked
More than 5 years have passed since last update.
Promise と async/await を始めからていねいに
最近、会社のフロントじゃない人にjavascriptを教えていて
「Promiseとはなんぞや?」という所から、
「async/awaitってのがあってね」という所まで解説しました。
非同期を制するものはjavascriptを制す、ということで、
その時の説明を記事にしておきます。
Promiseの使い方
まず、Promiseは次のような書き方をして使います。
newPromise((resolve)=>{resolve('Hello, World');}).then((result)=>{console.log(result);// Hello, World}).catch((error)=>{console.log(error);});
- 最初の関数の中で、resolveを実行すると、次のthenの関数に処理が進みます。
- resolveに渡した引数が、次のthenの引数として渡ります
- 処理のどこかでerrorが起きた場合、catchの関数に処理が進みます
これがPromiseの基本的な動きです
Promiseの使いどころ
Promiseをどういうとき使うのかの話。
基本的には非同期な処理をするときに使います。
setTimeout(()=>{console.log(1);},1000);console.log(2);
これは非同期なので、2 → 1と表示されます。
しかし気持ちは 1 → 2の順で表示したい、という場合。
ここでPromiseを使うと次のように書けます
newPromise((resolve)=>{setTimeout(()=>{console.log(1);resolve()},1000);}).then(()=>{console.log(2);})
これで、1を表示したら、resolveして2を表示するように、
上から下へと順番に処理を読めるようになりました。
Promiseで並列処理
複数の非同期を扱いたい、というケースでもPromiseが役立ちます。
例えば次のように書いたとして、これは3つともほぼ同時に処理を開始します。
setTimeout(()=>{console.log(1);},3000);setTimeout(()=>{console.log(2);},2000);setTimeout(()=>{console.log(3);},1000);
ここで「3つの処理が全て終わったとき、次の処理に進む」というコードを書きたいとします。
こういう場合 Promise.all が活躍します。
Promise.all([newPromise(function(resolve){setTimeout(resolve,1000);}),newPromise(function(resolve){setTimeout(resolve,1000);}),newPromise(function(resolve){setTimeout(resolve,1000);}),]).then((results)=>{console.log(results[0]);// 1つめのPromiseのresolve結果console.log(results[1]);// 2つめのPromiseのresolve結果console.log(results[2]);// 3つめのPromiseのresolve結果})
Promise.allには、Promiseの配列を渡します。
全てのPromiseがresolveされると、次のthenに進みます。
次のthenの引数には、各Promiseのresolveで渡したものが、配列として渡されます
async/awaitの使い方
async/awaitは、Promseを別の方法で書けるようにしたものと思えばOKです。
例えば
newPromise(resolve=>{resolve('foo');}).then(result=>{console.log(result);// foo})
これが
constresult=awaitnewPromise(resolve=>{resolve('foo')});console.log(result);// foo
というように書けるようになります(このコードだとエラーでまだ動きません)。
awaitの後ろには何かしら値を取ればよいので、よくPromiseを返す関数と組み合わせて使います。
functionfoo(){returnnewPromise(resolve=>{resolve('foo')})}constresult=awaitfoo();console.log(result);// foo
そしてawait文を使うには、そのスコープがasyncのついた関数でなければなりません。つまり
constmain=async()=>{functionfoo(){returnnewPromise(resolve=>{resolve('foo')})}constresult=awaitfoo();console.log(result);// foo}main();
こんな感じでasyncのついた関数の中でawaitが使えるようになります
基本的に、awaitを付けた関数は、記述した順に実行されるので
constmain=async()=>{functionfoo(){returnnewPromise(resolve=>{resolve('foo')})}functionbar(){returnnewPromise(resolve=>{resolve('bar')})}constresultFoo=awaitfoo();constresultBar=awaitbar();}main();
と書いたらfooがresolveしたあとにbarを実行します。
こうしてPromiseを使った非同期処理が、普通の同期処理っぽく書けるようになりました。
async/awaitのエラー処理
Promiseで処理に失敗した場合はcatchを使っていましたが、
async/awaitではtry/catchが使えます。
try{awaitfoo()}catch(e){throwe;}
このように、エラー処理も同期処理のように書けるようになります。
書いてみたはいいものの・・・
実際は業務で書いてるコードをもとに説明していたんですが、
実際のコードを抜くと、やっぱり説明しずらいかった。
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme