スマートフォンなど画面が小さい場合、Webサイトはどうしても縦長になりがちです。そしてスクロールを下まで行い、別なページに移ろうと思うと今度は上までスクロールし続けないといけません。これは非常にストレスです。
そこで使ってみたいのがヘッダー部分を固定表示してくれるライブラリです。ヘッダー情報へのアクセスがしやすくなるのでユーザビリティが高くなるでしょう。
simple-sticky-header

以下のようなシンプルなコードでヘッダー固定化を実現します。デザインがスクロール時には変更されます。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| fixednav({id:"outer-id", nav:"nav-id", distance:40, down:50, up:200, delay:70}); |
AnimatedHeader

スクロールするとヘッダーが小さくなるアニメーションとともに固定化されます。若干実装が複雑になっています。
partly-fixed-header

ヘッダーではなく広告を常時表示するのを想定して作られています。とは言えメニューなどに変更して使うこともできるはずです。
skimmet/header: A head that is only fixed when scrolled past the banner.
aheader

上にスクロールする際にヘッダーを表示するというタイプのライブラリです。ユーザが下までコンテンツを見た後、上に戻る操作をしたタイミングでヘッダー情報が出せます。
Peleg/aheader: Fixed header on scrolling up
こうしたライブラリを使うと画面上の一部分は常に情報が表示され、そこを使えなくなってしまいます。画面が十分に大きい場合はいいですが、4インチくらいの画面サイズであまり大きなヘッダーは却って邪魔になってしまう可能性があります。小さくユーザビリティを損なわないライブラリを選ぶか、二段階くらいのサイズ変化に対応しているものを選ぶと良いでしょう。
3月20日にDeviceConnectをhifiveから使うハンズオンを開催しました。
hifive x DeviceConnectによるWoT体験ハンズオン! – connpass

DeviceConnectというのはスマートフォンアプリで、スマートフォン内にHTTPサーバを立てます。そのHTTPサーバを使って、スマートフォンに繋がっている各種デバイス(THETA、Pebble、Hueなど)をWeb API経由で操作できる技術になります。
今回はデスクトップ側のHTMLファイルからDeviceConnectに接続し、スマートフォンの操作(バイブレーションやライトなど)を行うハンズオンを開催しました。資料はオンラインで公開しています。
hifivemania/deviceconnect-handson
DeviceConnectを使うためにはローカルネットワークが使えないといけないため、ハンズオン会場でルータを設置する必要がありました。また、本来であればデバイスを操作すると楽しいのですが、全員がHueなどを持っている訳ではないので、今回はスマートフォンの機能を操作するだけに留めています。DeviceConnectを知った上で、自分の持っているデバイスを操作してみて欲しいです。
今回のハンズオンではhifiveのコントローラ、並びにロジックを体験してもらいました。ロジックを使うことで非同期処理を分かりやすく、再利用性高く実装できます。hifiveには他にもたくさんの機能がありますので、ぜひ使ってみてください。
約10名の方に参加いただき、皆さんにhifiveはもちろん、DeviceConnectを使ったWoTの実現方法について学んでもらうことができました。hifiveではこれからもハンズオンを開催していきますので、ぜひご参加ください。
HTML5.1が勧告され、すでにW3Cの活動は5.2策定に向けて動き始めています。コンマ1の違いなので、大幅に変わるわけではありませんが、現状に合わせて多少の変更が行われるようです。
実際の勧告案はHTML 5.2にて確認できます(最終アップデートは2017年12月14日)。具体的なHTML5.1との違いは HTML 5.2: Changes にて確認できますが、今回は簡単にまとめてみました。以下の内容はあくまでも執筆時点のものであり、変わる可能性もあります。
menuタグのtoolbarの廃止
現状、Webブラウザでサポートしているものがないようで、このままいくとHTML5.2ではなくなるようです。
dialog タグの廃止
こちらも実装されているブラウザがない(Operaだけ実装されている模様)ようで、このまま進むと廃止対象になります。
複数のAPIの廃止
以下のAPIが廃止されます。
- registerContentHandler
Webサイトを特定のMIMEタイプに関連付ける。 - isContentHandlerRegistered
特定のMIMEタイプに関連づけられているかどうか判定する。 - isProtocolHandlerRegistered
URLスキームとURLを引数として、そのハンドラー状態を返す。
input type=datetime 、 inputmode の廃止
フォーム入力系としてはこの二つがなくなる可能性があります。
keygenタグの廃止
Web Crypto APIの登場に伴ってGoogle Chromeでは削除されています。他のブラウザではまだ使えるようですが、他も追従していくものと思われます。
基本的には以上のようにタグや要素の廃止が多いようです。Webブラウザで元々実装されていなかったものもあるので、仕様を最適化していると思われます。動作ががらっと変わることはないでしょうが、今後のWeb標準の変化を知るためにもチェックしておきましょう。
フォーム入力は必要とは言え、ユーザにとってストレスの大きい仕組みです。特にスマートフォンやタブレットで多くの入力項目を埋めるのは苦労が伴うでしょう。それだけにサーバに送って入力エラーがあると、ユーザは修正するのが嫌になってしまうかも知れません。
そこで使ってみたいのが今回紹介するクライアントサイドでの入力チェックライブラリです。リアルタイムに入力チェックを行ったり、そのメンテナンスを簡単にしてくれるライブラリが多数あります。
json-editor

JSONスキーマからフォームを生成します。JSONスキーマでは入力タイプであったり、入力できる文字列長なども指定できます。それらの設定に基づいて自動的にフォームを生成し、入力チェックも行ってくれます。
jdorn/json-editor: JSON Schema Based Editor
Winterfell

独自のJSONフォーマットからフォームを生成し、入力チェックも行うReact用のライブラリになります。独自のJSON記法を覚える必要がありますが、ステップ踏んで進むようなフォームも生成できるようです。
andrewhathaway/Winterfell: Generate complex, validated and extendable JSON-based forms in React.
formAnimation

バリデーションでエラーがあった際にフォーム全体を揺らすライブラリです。バリデーション自体はjQuery Validateを使っています。
nnluukhtn/formAnimation: Form Animation: when form validation <3 animate.css
tlx-editor

入力チェックを兼ね備えたWebコンポーネントライブラリです。一行テキスト、テキストエリア、ラジオ、チェックボックス、セレクト、スターレーティングなど幅広く対応しています。
Hyperform

HTML5のバリデーションAPIの完全実装を目指して作られているライブラリです。IE9についても利用できるようになっています。将来的にHTML5のバリデーションが標準化された後もそのまま使い続けられそうです。
hyperform/hyperform: Capture form validation back from the browser
jq-idealforms

基本的なバリデーションに加えて日付ピッカーやステップ、Ajaxによるサーバ側とのデータ検証など幅広い入力チェックに対応しています。ブラウザもIE9以降で利用できます。
elclanrs/jq-idealforms: The ultimate framework for building and validating responsive HTML5 forms.
jquery.formance

入力フォーマット(クレジットカードや電話番号など)を指定することで、入力チェックと必要に応じてハイフン(-)などを自動入力してくれます。
Creditly

クレジットカード番号の入力チェックに特化したライブラリです。カード会社を判別して、それによってセキュリティコードの桁数を変えたり、チェックデジットの検証も行います。
wangjohn/creditly: An intuitive credit card form
入力チェックはサーバ側でも行う必要があるため、クライアントに特化しすぎているとサーバ側でのエラーチェックと差異が発生したり、メンテナンスが煩雑になります。JSONスキーマのような設定ファイルを使うことで、サーバとクライアント両方で共通したバリデーションが可能になるでしょう。
フォーム入力というのはユーザにとってストレスの大きい仕組みです。やたらと項目が多かったり、選択肢が多数あると途中で適当な入力になってしまうかも知れません。それは業務システムでも言えます。
そこでなるべくユーザストレスを減らせるようなライブラリを紹介します。これらを使えばフォーム入力の手間や煩雑さが改善されるはずです。
floatlabels.js

スマートフォンでは入力項目のところにあらかじめ何を入力すれば良いかプレースホルダーという仕組みで文字が表示されていますが、ユーザが入力を開始すると消えてしまって不便です。floatlabels.jsではプレースホルダーを別な場所にアニメーションしながら移動してくれるので忘れてしまったりすることなく入力が続けられるようになります。
probots-io/floatlabels.js: Follows the famous Float Label Pattern. Built on jQuery.

fontIconPicker
Web Fontを使った入力補完ライブラリです。絵文字入力とはまた違った使い勝手になります。ユーザにアイコンを選択して欲しい時に使うと便利そうです。
paperstencil

PDF並の細かな表示にこだわったフォームが生成できます。作成ツールも用意されていますので、文章を書いたり入力欄を作ったりするのも簡単にできます。表を使った段組にも対応しています。
Garlic.js

入力内容をlocalStorageに保存しておき、再読込時に入力を再現してくれるライブラリです。入力項目が多い場合など、誤った削除でせっかくの入力内容がすべてなくなってしまったりするのを防げます。
formBuilder

ドラッグ&ドロップでフォームを作成できるライブラリです。生成した内容をデータベースなどに保存しておくことで、ユーザが自分自身で自由にフォームを作れるようになります。お問い合わせフォームのような汎用的なフォームで、ユーザがカスタマイズしたいといった時に使えます。
kevinchappell/formBuilder: A jQuery plugin for drag and drop form creation
Multi-Step-Form-Js

フォーム入力を複数の段階に分けたマルチステップにしてくれるライブラリです。本当のフォームは一つしかありませんので、サーバサイドに送られるのはすべて入力した後になります。入力項目が多い場合に使えます。
mgildea/Multi-Step-Form-Js: Multi Step Form with jQuery validation
formbase

フォームの表示をスタイルシートだけで見栄えのいいものにデザインしてくれます。HTMLは標準のままなので、作業不要です。単純なHTMLでデザインが当たっていない状態であれば、formbaseを使って少しでも使い勝手を向上すると良いでしょう。
electerious/formbase: Better default styles for common input elements.
見た目を整えるだけでもフォーム入力はしやすくなります。さらにJavaScriptによって改善できるポイントは多いでしょう。これらのライブラリを使いこなせばユーザにとって使いやすいフォームが簡単に実現できるはずです。
HTML5 APIで追加されつつも、あまり使われている雰囲気がないのがWeb Workerではないでしょうか。使い方はそれほど難しい訳ではないですが、別途JavaScriptファイルを用意したりするのが面倒に感じられるのかも知れません。JavaScriptではできない、並列処理を行うためには必須のAPIです。
そこで手軽に使えるようにインラインのちょっとしたコードをWeb Worker化してみたいと思います。
サンプルのコード
例えば次のようなコードをWeb Worker化します。 e はメインスレッドから渡されるイベントで、 e.data でメッセージを受け取れます。メインスレッドからは onmessage が呼ばれますので、処理を行った上で postMessage で返せばOKです。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| onmessage = (e) => { | |
| setTimeout(() => { | |
| postMessage('Workerからの返信です。' + e.data); | |
| }, 2000); | |
| } |
このコードを文字列にします。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const code = ` | |
| onmessage = (e) => { | |
| setTimeout(() => { | |
| postMessage('Workerからの返信です。' + e.data); | |
| }, 2000); | |
| } | |
| `; |
Blob化する
次にこの文字列をBlobにします。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const blob = new Blob(); | |
| const blobURL = URL.createObjectURL(blob); |
ワーカーにする
後はこのblobURLをワーカーとして指定するだけです。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const worker = new Worker(blobURL); |
これで準備完了です。
実行する
後はメインスレッドからpostMessageを使ってWeb Workerを呼び出します。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| worker.postMessage("Execute"); |
逆にワーカー側からメッセージが来た時にはonmessageが呼ばれます。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| worker.onmessage = (e) => { | |
| alert(e.data); | |
| }; |
このようにメインのJavaScriptファイルの中からWeb Workerを動的に作れるなら使い道があるかも知れません。なおWeb WorkerはDOM操作やwindow/documentオブジェクトへのアクセスができないので注意してください。ネットワークへのアクセスはできるので、バックグラウンドで通信を行うと言った時には良さそうです。
デモをJSFiddleにアップしてありますので参考にしてください。
HTML5になって進化しきった訳でも、進化しなくなる訳でもありません。新しい仕様がさらに進められています。それらはHTML5.1として策定されています。
今回はそのHTML5.1の中の入力チェック機能、HTMLInputElement.reportValidityについて紹介します。
HTMLInputElement.reportValidityとは?
仕様はHTMLFormElement.reportValidity() – Web APIs | MDNにまとめられています。まだ実装されているブラウザはGoogle ChromeやFirefoxくらいになります。HTML5で新しく追加された入力属性を使って入力値の検証を行えるAPIになります。
主な入力チェック
よく使われるものとしては以下があります。
必須チェック
required 属性で指定します。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <input name="name" type="text" required /> |

パターン
正規表現を使って入力パターンを指定します。この場合、title属性も使ってエラー時のメッセージを指定するのが良いようです。以下はごく簡単な電話番号チェック(国際電話には対応していません)です。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <input name="tel" type="tel" id="tel" pattern="[0-9]{2,4}-[0-9]{3,4}-[0-9]{4}" /> |

確認入力
メールアドレスやパスワードなど、確認入力を行う場合のチェックは以下のように実装します。まずHTMLにはメールアドレス入力欄が二つあったとします。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <div class="form-group"> | |
| <label for="exampleName">メールアドレス</label> | |
| <input name="email" type="email" id="email_addr" required /> | |
| <small id="emailHelp" class="form-text text-muted">必須</small> | |
| </div> | |
| <div class="form-group"> | |
| <label for="exampleName">メールアドレス(確認)</label> | |
| <input name="email_addr_repeat" type="email" id="email_addr_repeat" required /> | |
| <small id="emailHelp" class="form-text text-muted">確認入力</small> | |
| </div> |
そして確認入力のメールアドレスに対する入力イベントを使って検証を行います。エラーになる場合は該当要素の setCustomValidity メソッドに対してメッセージを適用します。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var mail = document.getElementById("email_addr_repeat"); | |
| mail.addEventListener("input", function() { | |
| if (this.value != document.getElementById('email_addr').value) { | |
| this.setCustomValidity('確認入力されたメールアドレスが異なります'); | |
| } else { | |
| // input is valid — reset the error message | |
| this.setCustomValidity(''); | |
| } | |
| }); |

入力チェック
入力チェックは任意のタイミング(フォーカスが外れた時、フォーム送信時など)で可能ですが、例えばフォーム送信時であれば次のように実装します。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var elem = document.getElementById("form"); | |
| elem.addEventListener("submit", function() { | |
| elem.reportValidity(); | |
| }); |
elem.reportValidity() が true(エラーがない)場合はフォーム送信されます。エラーがあれば送信は行われず、処理が終了します。
イベント
エラーがあった場合には該当フォームの invalid イベントが呼ばれます。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var elem = document.getElementById("form"); | |
| elem.addEventListener('invalid', function() { | |
| // メッセージや処理など | |
| }, false); |
実装デモをJSFiddleにアップしてあります。実際のコード、操作について確認してください。
まだ先進的なAPIだけに、EdgeやSafariでは実装されていません。とは言え、今後サポートするブラウザが増えてくれば利用できる場面が多そうなAPIです。
SVGはHTML5になって標準化された画像フォーマットです。バイナリではなくベクターなので、拡大縮小に強いのが特徴です。しかしXMLベースなので手書きで作るのは難しく、専用のソフトウェアを使ってSVG出力するのが一般的です。
今回はそうした専用ソフトウェアを用意することなくSVGを作成できる、WebブラウザベースのSVGエディタを紹介します。
Mondrian

簡単なドロー機能を備えています。出力はPNGまたはSVGで可能です。レイヤー機能はありませんが、色や線の太さを変えるといった操作は可能です。テキストも利用できます。生成したコンテンツをWebサイトに埋め込むためのコードを出力させる機能もあります。
artursapek/mondrian: Web-based vector graphics editor
Curve App

Webベースではありませんが、Electron向けなのでHTML5/JavaScript/CSSで作られています。円や多角形、ベジュ曲線を使ったドローにも対応しています。テキストは用意されていませんが、ごく簡単なドローであれば十分でしょう。
benogle/curve-app: Vector drawing desktop application
SVG-Floorplan-Editor

HTML5製のフロアマップエディタです。ドアを追加したり、壁を描けるようになっています。フロアマップに特化しているので会社の図面を作ったりするのが簡単です。
oodavid/SVG-Floorplan-Editor: Simple SVG Floorplan Editor for a client
jsvectoreditor

本格的に使えるSVGエディタです。直線、四角、円、曲線、文字、画像、連続線、テキストなどが用意されています。もちろん配置したオブジェクトの移動や回転もできます。保存機能はなく、XML(SVG)が表示されます。これをコピーしておけばまた再現も可能です。
Google Code Archive – Long-term storage for Google Code Project Hosting.
SVG-edit

ツールバーの並びなどがローカルアプリケーション風になっており、使い勝手の良いSVGエディタです。文字や円、四角、直線、ベジュ曲線、自由線が描けます。描画後、オブジェクトの移動や色の変更などもサポートされています。レイヤーにも対応しています。
SVG-Edit/svgedit: Powerful SVG-Editor on your browser
Webブラウザベースなので、作成したSVGがそのまま表示できると考えて良いでしょう。機能はエディタによって異なるので、目的にあったものを選ぶ必要があります。
画面の高画質化が進むのに合わせて、画像などのバイナリではサイズの肥大化が大きな問題になっています。SVGは拡大表示しても綺麗なまま、ファイルも1ファイルで済みますので今後SVGがメインフォーマットとして活用されていくのではないでしょうか。
SVGはドロー系描画機能として今後、ますます高画素化の進むディスプレイに対するリソースの肥大化問題の解決策として注目が集まっていくでしょう。PNGやJPEG画像ではキャンバスサイズが大きくなるにつれてファイルサイズが肥大化しますが、SVGはベクターデータなのでファイルサイズは変わりません。
しかしSVGの魅力は静的な表示を行うだけに限りません。JavaScriptで内容を変えたり、動かしたりできます。今回はそうしたSVGアニメーションを助ける各種ライブラリを紹介します。
flubber

特定の形から別な形へ自然にモーフィングできるライブラリです。一つから一つの場合、一つから複数(またはその逆)とアニメーションが可能です。
veltman/flubber: Tools for smoother shape animations.
Shape Shifter

二つのSVGファイルを指定して、アニメーションを生成してくれるライブラリです。Web上でファイルを指定できるので、プレビューを見ながら細かく調整ができます。
zPath.js

一つのSVGファイルを描画するアニメーションを生成します。描画の方法についても数パターン用意されています。
ZetCoby/zPath.js: A jquery plugin that will animate/draw SVG
mo · js

SVGベースのモーションアニメーションを生成するライブラリです。派手なアニメーションもできますが、クリック時に光らせたり跳ねるような、ちょっとしたアニメーションも可能です。
mo · js – Motion Graphics For The Web
Primitive

指定した写真を幾何学模様で描くソフトウェアです。その描画されていく過程をSVGファイルで保存できます。
fogleman/primitive: Reproducing images with geometric primitives.
Yarrow

矢印をアニメーションで表示したり、ツールチップを表示したりします。グラフなどと連携することによって見た目にインパクトのあるプレゼンテーションが実現できます。
Yarrow — svg animated arrow pointer and tooltip
jQuery DrawSVG

jQueryのプラグインとして提供されています。SVGの内容をアニメーションしながら描画するライブラリです。
Animate Plus

CSS3またはSVGでアニメーションを実現します。イージングが用意されているので、細かい動きを実装することなくアニメーションが実現できます。
bendc/animateplus: CSS and SVG animation library
SVG Morpheus

指定したアイコンが別なアイコンに変化するアニメーションを生成します。アイコンとイージング、遅延、回転などの設定だけなのですぐに使いこなせるでしょう。
vivus

SVGファイルを解析し、その描画をアニメーションしながら順番に行っていきます。
maxwellito/vivus: JavaScript library to make drawing animation on SVG
Walkway

基本的に一筆書きの容量で既存のSVGファイルからアニメーションを生成します。
ConnorAtherton/walkway: An easy way to animate SVG elements.
Letterbolt

SVGで文字を描くライブラリです。スクロールなどのイベントに合わせてスムーズに文字が描かれます。
Loading…

SVGでローディング表示を行います。ドットやラジオなど、8種類のアニメーションが用意されています。
jxnblk/loading: This could take a while
Animated SVG Icons

20種類を超えるアニメーションするSVGアイコンを提供しています。Snap.svgというライブラリを用いています。
Lazy Line Painter

一筆書きでアニメーションするライブラリです。SVGファイルは先頭と終端がなければならず、後はファイルを指定するだけでアニメーションしながら描画していきます。jQuery/Raphaëlが必要です。
camoconnell/lazy-line-painter: A jQuery plugin for path animation using the CSS –
SVGはマウスイベントなどが利用でき、形や色を変えたりできます。ユーザイベントに反応することでユーザビリティを高めたり、注目して欲しい情報を目立たせる効果が期待できるでしょう。
ES7ではPromise処理を簡素化できるawait/asyncが追加されています。これによってコールバック地獄からPromiseによって抜け出せたとの同様に、Promiseによる then/catch 地獄から抜け出せるようになります。
サンプルコード
例えば処理を2秒間遅らせて、その結果を取得すると言った処理を考えてみます。ES5で書くと次のようになるでしょう。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function delay(x, f) { | |
| setTimeout(function() { | |
| f(x); | |
| }, 2000); | |
| } | |
| function main() { | |
| delay(10, function(x) { | |
| console.log(x); | |
| }); | |
| } | |
| main(); |
さらにdelayを2回呼び出すと次のようになります。ネストが一つ深くなります。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function delay(x, f) { | |
| setTimeout(function() { | |
| f(x); | |
| }, 2000); | |
| } | |
| function main() { | |
| delay(10, function(x) { | |
| console.log(x); | |
| delay(20, function(y) { | |
| console.log(y); | |
| }); | |
| }); | |
| } | |
| main(); |
Promiseの場合
Promiseを使った場合は次のようになります。ネストが深くならず、thenを使ったメソッドチェーンが可能になります。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function delay(x, f) { | |
| return new Promise(function(res) { | |
| setTimeout(function() { | |
| res(x); | |
| }, 2000); | |
| }); | |
| } | |
| function main() { | |
| delay(10) | |
| .then(function(x) { | |
| console.log(x); | |
| return delay(20); | |
| }) | |
| .then(function(y) { | |
| console.log(y); | |
| }); | |
| } | |
| main(); |
awaitを使った場合
さらにawaitを使った場合です。ネストが一段減りますが、処理を行う関数を async で囲む必要があるので、最低一つの関数の中で処理する必要があります。Promiseだけの場合は必ずしもmain関数は必要ではありません。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function delay(x) { | |
| return new Promise(res => { | |
| setTimeout(() => { | |
| res(x); | |
| }, 2000); | |
| }); | |
| } | |
| async function main() { | |
| var x = await delay(10); | |
| console.log(x); | |
| var y = await delay(20); | |
| console.log(y); | |
| } | |
| main(); |
awaitのコードをES5に変換する
ではこのawaitのコードをBabelを使ってES2015のコードに変換した場合、どのようなコードになるのでしょうか。以下のコードはそのままnodeで動くわけではありませんが、次のようなコードが生成されます。
await的な機能を実現するために while ループを続けているのが分かります。ちなみにこれは delay を一回しか使っていない場合です。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| "use strict"; | |
| var main = function () { | |
| var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { | |
| var x; | |
| return regeneratorRuntime.wrap(function _callee$(_context) { | |
| while (1) { | |
| switch (_context.prev = _context.next) { | |
| case 0: | |
| _context.next = 2; | |
| return delay(10); | |
| case 2: | |
| x = _context.sent; | |
| console.log(x); | |
| case 4: | |
| case "end": | |
| return _context.stop(); | |
| } | |
| } | |
| }, _callee, this); | |
| })); | |
| return function main() { | |
| return _ref.apply(this, arguments); | |
| }; | |
| }(); | |
| function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | |
| function delay(x) { | |
| return new Promise(function (res) { | |
| setTimeout(function () { | |
| res(x); | |
| }, 2000); | |
| }); | |
| } | |
| main(); |
delay を2回実行すると次のようになります。 context.next/context.prevを使ってステータスを管理することで、ネストが深くなるのを防いでいます。ですが、switchで使っている数字は非同期処理が追加されるごとに数字が増えていきます。Babelの生成したコードを読み取るのは大変になっていくことでしょう。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| "use strict"; | |
| var main = function () { | |
| var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { | |
| var x; | |
| return regeneratorRuntime.wrap(function _callee$(_context) { | |
| while (1) { | |
| switch (_context.prev = _context.next) { | |
| case 0: | |
| _context.next = 2; | |
| return delay(10); | |
| case 2: | |
| x = _context.sent; | |
| console.log(x); | |
| _context.next = 6; | |
| return delay(20); | |
| case 6: | |
| x = _context.sent; | |
| console.log(y); | |
| case 8: | |
| case "end": | |
| return _context.stop(); | |
| } | |
| } | |
| }, _callee, this); | |
| })); | |
| return function main() { | |
| return _ref.apply(this, arguments); | |
| }; | |
| }(); | |
| function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | |
| function delay(x) { | |
| return new Promise(function (res) { | |
| setTimeout(function () { | |
| res(x); | |
| }, 2000); | |
| }); | |
| } | |
| main(); |
なお、Promiseの場合はrejectを使いますが、awaitを使った場合はtry/catchでエラー処理を行います。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| async function main() { | |
| var x = await delay(10); | |
| console.log(x); | |
| try { | |
| var y = await delay(20); | |
| console.log(y); | |
| }catch(e) { | |
| console.log('Error'); | |
| } | |
| } |
非同期処理はJavaScriptに付きもので、一番厄介な存在でしょう。しかしES7のawaitによってネストがなくなったり、変数を同じスコープで扱えるようになります。現在、デスクトップやスマートフォンのほとんどのブラウザでサポートされていますので(IE系は未サポート)、HTML5を活用する際にはぜひ使ってみてください。