最近のJavaScriptでは非同期処理をPromiseで書くことが増えています。コールバック地獄で苦しんできたフロントエンジニアとしてはとても書きやすくなっていることでしょう。
しかし次世代のJavaScript(ES2017)ではPromiseではなくAsync Functionsを使った書き方が主流になります。さらに言えば、現状のモダンなブラウザではPromiseとAsync Functionsのサポート具合はほぼ変わらなくなっています。Promiseをサポートしていて、Async Functionsをサポートしていないのは UC Browser for Android のみとなっています。IE11は元々使えませんし、Opera miniも技術的に難しいでしょう。
Can I use... Support tables for HTML5, CSS3, etc
そのため、Async Functionsをブラウザが対応していないから使わないという理由はなくなっています。そこで今回はJavaScript SDKを例にとって、Async Functionsをどう使うか紹介します。
Promiseの場合
データクラスに保存して、データを検索するデモコードです。初期設定は共通です。
const NCMB = require('ncmb'); const config = require('./config'); const ncmb = new NCMB(config.applicationKey, config.clientKey); const Hello = ncmb.DataStore('Hello'); const hello = new Hello();
ではまずPromiseの場合です。saveメソッドやfetchAllメソッドを使うとPromiseオブジェクトが返ってきますので、thenで処理をつなげられます。
hello .set('message', 'Hello World') .save() .then(result => { console.log('保存しました'); return Hello .equalTo('message', 'Hello World') .fetchAll() }) .then(results => { console.log(results); }) .catch(err => console.log(err));
Async Functions
Async Functionsを使う場合、まず関数をasyncで囲む必要があります。
const main = async (ncmb) => { } main();
この関数の中ではawaitが使えるようになります。awaitはPromiseで言うところのthenの結果が返ってくるものです。そしてcatchは例外処理として送られます。つまりtry/catchで処理を囲む必要があります。今回は分けていますが、もちろん一つのtry/catchでもいいです。
try { await hello .set('message', 'Hello World') .save(); console.log('保存しました') }catch(e) { console.log(`Save error. ${JSON.stringify(e)}`); } try { const results = await Hello .equalTo('message', 'Hello World') .fetchAll(); console.log(results); }catch(e) { console.log(`Search error. ${JSON.stringify(e)}`); }
その結果、全体では次のようになります。
const main = async (ncmb) => { try { await hello .set('message', 'Hello World') .save(); console.log('Save successful.') }catch(e) { console.log(`Save error. ${JSON.stringify(e)}`); } try { const results = await Hello .equalTo('message', 'Hello World') .fetchAll(); console.log(results); }catch(e) { console.log(`Search error. ${JSON.stringify(e)}`); } } main();
まとめ
全体の処理をasyncで囲むところと、try/catchで囲むところでネストが二つになってしまいます。Promiseの場合は1つだったので、全体としては1つネストが深くなっていると言えます。ただし、Promiseの場合、次の処理に繋がるところをPromiseオブジェクトを返してthen処理を呼ぶ必要があったので、若干見づらさを感じるところがありました。それに対してAsync Functionsの場合は別々にtry/catch書けるのでエラーの補足もしやすく、コードが見やすくなっていると感じます。
後は同期的に書けるので、Promiseをまたいだ時に変数をどう受け渡すかといった問題がなくなります。
hello .then(a => { console.log(a); // => 何か値 return async_func(); }) .then(b => { // ここではaにアクセスできない console.log(a); // => undefined })
好みに分かれるところはありますが、モバイルブラウザなどではAsync Functionsが標準で使えるようになっていることを考えると、今後はAsync Functionsを使った書き方がデフォルトになっていくのではないでしょうか。