ES2017と非同期と取り残されたIE11
SPA(SinglePageApplication)のような仕組みを用いたJavaScriptでフロントエンド開発では、Ajaxによる非同期通信を頻繁に行うことになります。この際、非同期で生じる多数のコールバックによって、プログラムの可読性が下がる結果となります。しかしES2017ではasync/awaitが導入され、ほとんどのブラウザでは標準的に動くようになっています。しかしそこに取り残されたものがいます。IE11です。
IE11対応の必要性とGoogleBot
開発元のMicrosoft自身がEdgeに移行することを推奨しており、新規案件では対応の必要性は皆無と言って良いでしょう。しかしIE11での必要性は無くてもIE11で動くようにしておいた方が良い決定的な理由があります。それはIE11で動くものはGoogleBotが理解できる、つまりSEO対策が行えるということです。ES2017をフロントエンド開発に用いた時点で、そこはGoogleBotが理解できないサイトと化すのです。GoogleBotの動作確認を最も簡単に行うには、仕様の古いIE11を用いるのが一番簡単なのです。
そもそもIE11でPromiseが使えない
async/await以前の問題として、IE11ではPromiseが使えません。正確には標準対応していません。ただし使う方法はあります。HTMLの中に以下のコードを入れるだけです。
<script type="text/javascript" src="https://www.promisejs.org/polyfills/promise-7.0.4.min.js"></script>
無いものを無理矢理実装してくれるものなので、ネイティブで対応している環境に比べれば当然のごとくオーバーヘッドがあります。しかし元々非同期が必要なケースは遅延実行が核となるので、大した問題にはなりません。
async/awaitはどうするの?
さきほどの内容でIE11でPromiseは使えるようになりました。次はasync/awaitです。この部分にはいくつかの選択肢があるのですが、一番簡単なのはTypeScriptを使うついでにトランスコンパイルしてもらうことです。
{
"compilerOptions": {
"sourceMap": true,
"target": "es5",
"module": "commonjs",
"lib": [
"es2015",
"dom"
]
}
}
targetをes5にしておけば、await/asyncがトランスコンパイルでIE11でも使用可能となります。対応作業はこれだけです。当然ですがTypeScriptのコンパイル環境が必要となるので、Node.jsとTypeScriptのモジュールはインストールが必要です。
サンプルソース
サンプルコード置き場
サンプル解説
このサンプルでは非同期の同期が分かりやすいようにSleep()による待機機能を作りました。1秒ごとに出力という文字列がブラウザに追加されていきます。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
<script type="text/javascript" src="https://www.promisejs.org/polyfills/promise-7.0.4.min.js"></script>
<script type="text/javascript" src="index.js"></script>
<title>TypeScript_await_ie11_test</title>
</head>
<body>
</body>
</html>
/**
* 待機用関数
* timeout 待機時間(ms)
* @returns Promise<void>
*/
function Sleep(timeout:number) : Promise<void>{
return new Promise((resolv)=>{
setTimeout(()=>{
resolv()
}, timeout)
})
}
/**
*メイン関数(非同期)
*@returns Promise<void>
*/
async function Main() : Promise<void>{
const div = document.createElement('div')
document.body.appendChild(div)
while(true){
div.innerHTML += '出力<br>'
await Sleep(1000)
}
}
//ページ読み込み時に実行する処理を設定
addEventListener("DOMContentLoaded", Main)
非同期系の機能をPromise化してしまえば、後は昔ながらの線形的な書き方でプログラムを記述することが出来ます。
まとめ
現在、Webシステム用のオリジナルフレームワークを作っています。フロントエンド側のライブラリがある程度が仕上がり、こっちはまだ未公開ですがNode.jsで開発しているバックエンド側の実装もある程度すすみました。
その中で、フロントエンドからバックエンドへの通信機能を、ローカル関数を呼ぶかのごとく簡単に使える機能の実装しました。そこで今回のawait/asyncを使っています。結果としてとんでもなく記述が簡単になりました。
そのあたりの内容は、サンプルアプリケーションを一本作ってから記事にしたいと思います。