こんにちは。ShareWisのソフトウェアエンジニアのフンです。
この記事ではJavascriptにおける async/awaitについて紹介したいと思います。
async/awaitパターンとは多くのプログラミング言語で用いられる構文上の特徴であり、非同期、ノンブロッキング関数を通常の同期関数と同様の方法で構造化することができるようにするものです。
Javascriptにおける async/await とは?
async/await
を使用することで同期的に見えるJavaScriptのコードを非同期的に動作するように記述することができます。async とは?
asyncとは任意の関数の前に追加することができるJavascript の予約語の一つです。asyncを関数の前に置くとその関数がPromiseオブジェクトを返すようになります。
async関数を使用するには、以下のように従来のプロミス/コールバックの記法で記述します。
async function getCompanyName () {
return "Share-Wis Company"
}
明示的にPromiseオブジェクトをを指定することも可能です。下記は上記のコードと同じ値を返します。
async function getCompanyName() {
return Promise.resolve("Share-Wis Company")
}
getCompanyName()を使用するには関数が返すPromiseオブジェクトを使用します。getCompanyName()の処理が成功すると、then()関数が発火しコールバックが呼び出されます。
getCompanyName().then((res)=> {
console.log('Company name:',res)
})
実行結果を見てみましょう。
awaitとは?
awaitを非同期呼び出しの前に置くことで、特定のPromiseの処理が終了するのを待ち、終了を確認した後、非同期ブロックの実行が行われます。
awaitの書き方
let value = await promise;
awaitはasyncが使用されていない関数内で使用できない
もし非同期ではない関数内でawaitを使おうとすると、シンタックスエラーが発生します。
function f() {
let promise = Promise.resolve(1);
let result = await promise; // Syntax error
}
await
は非同期の関数内でのみ動作します。
async function getCompanyName () {
return "Share-Wis Company"
}
async function callingFunction() {
const companyName = await getCompanyName()
console.log('Company name:',companyName)
}
callingFunction()
await
は、async
ブロックの処理が終了するのを待つだけで、他の処理を止めたりはしません。async/awaitはPromise.allとの相性が抜群
await
とPromise.all
で関数を囲ってやることで複数の処理を記述することができます。let results = await Promise.all([
fetch(url1),
fetch(url2),
...
]);
エラーが発生した場合は、失敗したPromiseからPromise.allに通常通り伝搬され例外となるので、try…catchを使って呼び出しの周りでキャッチすることができます。
async/awaitブロック内でのエラーの取り扱い
async function handlingError() {
await Promise.reject(new Error('error'))
}
または、
async function handlingError() {
throw new Error("error");
}
async function handlingError() {
try {
const response = await fetch('api-url')
} catch(err) {
console.log(err)
}
}
try..catch
, then the promise generated by the call of the async function handlingError()
becomes rejected. We can append .catch
to handle it:async function handlingError() {
const response = await fetch('api-url')
}
// handlingError() 例外処理発生時にPromiseが拒否される
handlingError().catch(alert)
.catch
をつけ忘れると例外処理発生時に、上記画像の様にhandlingErrorが発生します。最後に
awaitを関数の前に置くことで出来るようになること
- 常にPromiseオブジェクトをreturnで返すようになります。
- その関数ブロック内でawaitが使用できるようになります。
Promiseの前にawaitを置くことで、その処理が終了するまで待ち、終了を確認した後に次の処理へと移ります
- エラーが発生すれば、そのエラーになった行からawaitを設置したPromiseに対してthrow errorが投げられる。
- 処理が正常に終了した場合は、結果を返却する。
async/await を使用するメリット
- コードの可読性があがる Promiseは大変便利なのですが、Promise単体で使用することで処理が数珠つなぎとなり、コードが煩雑になることが多いです。
- コードを簡潔に書くことができる処理ごとに関数を書くので、コードの再利用性が高くなります。
- デバッグが容易になる 各々の処理をブロック単位で書くことが出来るようになるので、Promise単体で使用するより場合に比べ、問題発生時にバグが発見しやすくなります。
(フン)
English version: How to use async/await of JavaScript
async/await
pattern is a syntactic feature of many programming languages that allows an asynchronous, non-blocking function to be structured in a way similar to an ordinary synchronous function.What is async/await in Javascript?
async/await
was added in the (ES2017+) release, it is syntactic sugar that makes it easier to write promises in JavaScript. async/await
helps you write synchronous-looking JavaScript code that works asynchronously.
What is async?
async
is a keyword that can be added before any function. Once it is placed before a function, it makes sure that the function returns a promise.
In order to use the async
function, you will have to resolve it using the traditional promise/callback
approach as depicted in the code below:
async function getCompanyName () {
return "Share-Wis Company"
}
We could explicitly return a promise, which would be the same:
async function getCompanyName() {
return Promise.resolve("Share-Wis Company")
}
Use the promise object returned by the function.The then()
function, success callback is invoked once the promise resolved.
getCompanyName().then((res)=> {
console.log('Company name:',res)
})
Let see the output blow:
What is await?
await
is another keyword in JavaScript which can be used in a block declared as async
. await
has no meaning or existence without async
.
Once you place await
before any asynchronous call, it will wait for that particular promise to settle and then further execution from the async
block will take place.
The syntax
let value = await promise;
Can’t use await in regular functions
If we try to use await
in a non-async function, there would be a syntax error:
function f() {
let promise = Promise.resolve(1);
let result = await promise; // Syntax error
}
await
only works inside an async
function
The error will show
Let improve the above example by using await
async function getCompanyName () {
return "Share-Wis Company"
}
async function callingFunction() {
const companyName = await getCompanyName()
console.log('Company name:',companyName)
}
callingFunction()
The output:
await
only makes sure an async
block waits. it does not stop Javascript from executing other operations.
async/await works well with Promise.all
When we need to wait for multiple promises, we can wrap them in Promise.all
and then await
:
let results = await Promise.all([
fetch(url1),
fetch(url2),
...
]);
In the case of an error, it propagates as usual, from the failed promise to Promise.all
, and then becomes an exception that we can catch using try..catch
around the call.
How can we handle errors in async/await block?
If a promise resolves normally, then await promise returns the result. But in the case of a rejection, it throws the error, just as if there were a throw
statement at that line.
async function handlingError() {
await Promise.reject(new Error('error'))
}
or use this:
async function handlingError() {
throw new Error("error");
}
Unlike promise
, await
does not have special handling features like catch/error
block for the errors. You have to use try/catch
block over the await statements as shown below.
async function handlingError() {
try {
const response = await fetch('api-url')
} catch(err) {
console.log(err)
}
}
If we don’t have try..catch
, then the promise generated by the call of the async function handlingError()
becomes rejected. We can append .catch
to handle it:
async function handlingError() {
const response = await fetch('api-url')
}
// handlingError() becomes a rejected promise
handlingError().catch(alert)
If we forget to add .catch
there, then we get an unhandled promise error (viewable in the console).
Conclusion
The async keyword before a function has two effects
- Makes it always return a promise.
- Allows
await
to be used in it.
The await keyword before a promise makes JavaScript wait until that promise settles, and then
- If it’s an error, an exception is generated — same as if throw error were called at that very place.
- Otherwise, it returns the result.
async/await brings the following advantages
- Code Readability
promise
chain or nested callbacks lead to a lot of confusion. The code becomes less readable. - Concise and clean code
async/await
have concise and clean codes and do the same thing - Debugging:As code is not divided into inner callbacks, developers can find it easy to go through lines and debug the code.
- Code Readability
(Hung)