Home

miniapp

サンプルで分かる! jQuery.Deferredで非同期処理入門

はじめに

最初に前置きが続いてしまうので、お急ぎの方は簡単な使用例 その1からどうぞ。

jsによる非同期処理の書き方として、CommonJSが提唱するPromises/Aというものがあります。

概念図などを見ると複雑に感じるのですが、実はとても簡単で、すぐにでも使えるものです。
jQueryにはこの機能が実装されていますので、jQuery.Deferredを使って解説します。

同期・非同期とは

まず最初に、あらためて同期と非同期の違いについて説明します。

同期とは、プログラムを記述した順に処理が実行される事です。
例えば、

console.log(0);
console.log(1);
console.log(2);

これは順に0, 1, 2と出力されます。

次に非同期ですが、これは必ずしもプログラムを書いた順に実行されません。

console.log(0);
$(document.body).on('click', function(){
      console.log(1);
  })
console.log(2);

これは0, 2が出力され、body要素をクリックしない限り1は出力されません。

これを同期、つまり書かれた順に処理が実行されるように記述できれば、
コールバックから開放され、処理があちこちに散らばる事が減り、より分かりやすく記述することが出来ます。

それを可能にするのがPromises/Aの概念です。

これはjqueryだけの機能ではありません。ここで解説した手法はjquery無しでも活用できます。
詳しくは最後のまとめに書きます

サンプルの触り方

下記のサンプルはjsFiddleを利用しています。
赤丸で囲った箇所をクリックして確認して下さい。
その場で動かして確認出来ます。
fiddle-img


簡単な使用例 その1

今回の基本のテンプレートとなるのが下記です。

上から順に0, 1, 2と出力されます。
次に非同期パートを混ぜてみます。

これだと上から順には実行されません。
0, 2, 1の順に出力されます。

これを同期で実行させるように書き換えてみます。

http://jsdo.it/m_hatayama/kJt3

こうなります。実際に実行すると
0が出力された後、1秒後に1,2と出力されます。

なぜ、こう書くと同期処理になるのかを解説します。

処理のブロックのやり方

下記コードを見て下さい。


実行すると、1を出力する処理まで進みません。
これは2個目のthenブロックでの処理のおかげです。

thenチェーンの中で新たにDeferredオブジェクトをreturnする事で、
そこで処理はブロックされます。

では、ブロックした処理を再開し、次のthenチェーンに進めるにはどうしたら良いのかを紹介します。


このようになります。d.resolve()するだけです。
するとDeferredの内部的には『解決』というステータスになり、次のステップへと進みます。

resolve()するタイミングは自由に調製できます。
もう一度、setTimeoutで遅延させる処理を見てみましょう。


2個目のthenブロックの中で、Deferredオブジェクトをreturnし、
1000ms後にresolveしてます。
これにより1000ms処理が止まり、その後に最後のthenブロックへと進みます。

処理をストップさせたかったら$.Deferred()をreturnし、
次に進めたかったらresolve()する。

このルールさえ覚えてしまえば、自由にコントロールできます。

簡単な使用例 その2

クリックされるまで処理をブロックさせる例です。

結果を次のブロックへ渡す

$.get()でxhrを使いファイルを読み込みます。
$.get()はdeferredオブジェクトを返すので、新たに$.Deferred()で生成する必要はありません。

注目する点は、読み込み後のブロックです。
引数resで、$.get()の結果が渡ってきています。
このように、次のブロックに値を渡す事が可能です。


このように複数渡すことも可能です。

エラー処理

例外処理の例です。
この場合は、resolve()ではなく、reject()を実行します。
すると、以降のthenブロックは無視され、failブロックへ処理が飛びます。
failブロックは必ずしも書かなくてOKです。

reject()は途中で処理をストップ(キャンセル)させたい場合にも使えます。

同時にいくつも実行し、全て終わったら次へ $.when

非同期処理を同時に実行し、全てが完了してから次の処理に進ませたい場合があります。
その場合、when()を使います。


画像を同時に2つ読み込み、両方の読み込みが完了したら次のブロックへ進ませる例です。
thenやwhenにはDeferredオブジェクトを渡せばOKです

ですので、今までの例では処理をthenブロックに書いていましたが、
今回は処理を纏めるため、thenブロックの中の処理を別の関数にまとめています。

いろいろ組み合わせる

最後に、これまで紹介した機能をまとめて実行してみます。
記述した順に処理されていく過程がよくわかると思います。

最後に

jqueryのDeferredを紹介しましたが、冒頭にも書いたとおり、commonJsが提唱している、Promises/A
という仕様をjqueryが実装したものです。

他にも同様の機能を使える、下記の様なライブラリがあります。

JSDeferred – Asynchronous library in JavaScript. Standalone and Compact
kriskowal/q · GitHub
cujojs/when · GitHub

Deferredを使えば、わかりやすく、非同期処理もすっきり記述することが出来ます。
いったん理解してしまえば、Deferredなしでの記述は有り得なくなるでしょう。

(※ この記事は弊社社内レポートに加筆・修正を加えたものです。)

jsDeferred, when.js, q.jsなどの非同期ライブラリでのエラー処理

最近、JSDeferred, when.js, q.jsなどの非同期処理を同期処理のように扱うライブラリが充実してます。

Promises/A – CommonJS Spec Wikiという、jsで非同期処理を書くならこうしましょう! っていう案があり、
それに即したライブラリがwhen.jsq.jsになるようです。
jsDeferredは厳密には違うようですが、目指す所や使い勝手はほぼ同じ。

これらのライブラリは非常に便利で面白いのですが、この3つに共通しているのが、エラーが起きてもライブラリ内部の処理でtry – catchしているので、ブラウザのconsoleにエラー出力されないという事。

0-9, JSDeferred使ってるコードをテストする

jsDeferredだと、このように

Deferred.onerror = function (e) {
    throw e;
};

としてやるとconsoleに出力されますが、throwした段階で処理が止まってしまいます。
returnした時と同じような挙動になる。
そうすると今度はライブラリのerrorだった時用のフォールバック処理が機能しなくなっちゃうという、もどかしい状態になってしまう。

なので、ライブラリのソースに手を加えないといけないのですが、
下のような回避策が考えられる。

jsdeferredの場合

_fire : function (okng, value) {
	var next = "ok";
	var error; //ここ
	try {
		value = this.callback[okng].call(this, value);
	} catch (e) {
		next  = "ng";
		error = value = e; //ここ。すぐにthrowせずにとっておく。
		if (Deferred.onerror) Deferred.onerror(e);
	}
	if (Deferred.isDeferred(value)) {
		value._next = this._next;
	} else {
		if (this._next) this._next._fire(next, value);
	}

	if(error) throw error; //ここでthrow
	return this;
}

//ここ って書いたとこみたいな処理いれてやるとか、

q.jsだと

function _fulfilled(value) {
    var error;

    try {
        return typeof fulfilled === "function" ? fulfilled(value) : value;
    } catch (exception) {
        error = exception;
        return reject(exception);
    }finally{
    	//finallyでやんなくてもいいけども

        if(error){
          setTimeout(function(){
            throw error;
          }, 0)
        };
    }
}

みたいにエラー起きてた時だけthrowするとか。
普通にthrowすると、またそこで処理が止まるのでsetTimeoutで非同期にthrowする。

when.jsも同じで、
try catchしてる所をこうする。

function fulfilled(value) {
		var p = new Promise(function(onFulfilled) {
//ここに同じような処理を入れる。
			try {
				return resolve(typeof onFulfilled == 'function' ? onFulfilled(value) : value);
			} catch(e) {
				return rejected(e);
			}
		});

		return p;
	}

非同期にthrowするから、出力するタイミングが完璧ではないとかあるけれども、
consoleに出力されなくてデバッグが出来ないっていうケースは回避される。

ブックマークレット版 sencha animator用 android対応 cssフォーマットツール

以前作ったsencha animator用 android対応 cssフォーマットツール – miniappを、ブックマークレットから実行するバージョン。

まず、下記の「sencha animator get css」リンクをブックマークして下さい。
リンクをブックマークバーにドラッグすればOKです。
sencha animator get css
senchaのプレビューHTMLを表示している時に先ほどブックマークしたブックマークレットを実行すれば、css部分を抜き出しandorid2対応用に変換、オーバーレイして表示します。

スクリーンショット 2013-01-28 23.42.21

jsで動的に動画を変形させる

作品はこちら
スクリーンショット 2013-01-22 23.16.24
※chromeでのみ確認。表示されない場合はリロードしてみて下さい。

以前Flashで作ったものをjsに移植したものです。
マウスで操作出来るスリットスキャン – miniapp
動画上をマウスでドラッグしたまま、色々動かしてみて下さい。
ジェイ・ケイがくるくる回って面白いです。

canvasを使えばflashのbitmap操作のようなことが可能です。
動画のキャプチャも可能なので、同じ事ができます。

ただFlashでは専用の便利なメソッドが色々と用意されていたのに比べ、jsは不便です。

過去jsdoitに投稿したのですが、動画をキャプチャするには同じドメインに動画が置いてないといけません。jsdoitでは動画のuploadは出来ないので、上手く動かすことができませんでした。

【documentElementByIdを使わずとも、ID要素にJavaScriptからアクセスできちゃう件】の対策

documentElementByIdを使わずとも、ID要素にJavaScriptからアクセスできちゃう件 | IDEA*IDEA

下記コードを動かしてみました。
html-id-access
ほんとだ!

<!DOCTYPE HTML>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0,user-scalable=0">
  <title>html-id-access</title>
</head>

<body>
  <div id="main">chromeのconsoleで見てくださいね</div>
  <div id="test_a"></div>
  <div id="test-b"></div>
</body>

  <script>
    console.dirxml(main);
    console.dirxml(test_a);
//    console.dirxml(test-b); エラー
  </script>

これを防止したい場合、変数名として使えない文字列 ‘-’ などをidに混ぜればアクセスできなくなるので、それで対策はできそう。

有効に使えるかもですが、知らないとハマりますね。恐ろしい。

[js] debuggerステートメントのtipsと、デフォルトとのイベントハンドラー・handleEvent

テストURL
debugger

ブレークポイント中のコンソール内this

プログラム内にdebuggerと書くとそこにブレークポイントを張れます。
コンソールからプログラムを実行したり、プロパティを表示したりできるのですが、コンソールに打ち込んだthisは、ブレークポイントで止まっている箇所を指します。
昨日教えてもらって初めて知りました。
下記のスクリーンショットを見ると挙動が分かると思います。

スクリーンショット 2012-12-16 0.19.59

デフォルトのイベントリスナ

addEventListenerの第2引数には通常関数を代入しますが、
handleEventというプロパティを持ったobjectを渡すこともできます。

スクリーンショット 2012-12-16 0.24.31

jsのprototypeを使って宣言したプロパティの注意点

prototypeで宣言したものは全インスタンスで共有されます。
メモリの節約になりますが、注意して使わないとダメですね。

動作確認ページ
prototyeの共有

そういえばprototypeはそういう意味だったのですが、躓いてしまいました。

分かった上でクラスの静的プロパティとして使うのはありなのかな。

sencha animator用 android対応 cssフォーマットツール

- – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – -
2013/01/28 追記: ブックマークレット版も作成しました。
ブックマークレット版 sencha animator用 android対応 cssフォーマットツール – miniapp
- – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – -

sencha css3 animation reformat

Sencha Animatorで書き出されるcssは、
androidでは動かない、または表示くずれを起こしてしまうプロパティが含まれています。

これをandroid(2.3以降)に対応させるcssに治すツールです。


スクリーンショット 2012-12-01 13.49.12

たとえば

@-webkit-keyframes ani-an-obj-1 {
    0% {
        -webkit-transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(1deg) scale3d(0.9, 0.9, 1);
        -webkit-animation-timing-function: cubic-bezier(0,0.54,0.6, 1);
    }

    8.4703% {
        -webkit-transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(-2deg) scale3d(1.15, 1.15, 1);
        -webkit-animation-timing-function: cubic-bezier(0,0.54,0.525, 1);
    }

    16.5062% {
        -webkit-transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale3d(0.95, 0.95, 1);
        -webkit-animation-timing-function: cubic-bezier(0,0.475,0.575, 1);
    }

    23.9405% {
        -webkit-transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(2deg) scale3d(1.05, 1.05, 1);
        -webkit-animation-timing-function: cubic-bezier(0,0.5,0.705, 1);
    }

    32.7952% {
        -webkit-transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale3d(0.95, 0.95, 1);
        -webkit-animation-timing-function: cubic-bezier(0,0.495,0.64, 1);
    }

    40.8074% {
        -webkit-transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(-1deg) scale3d(1.02, 1.02, 1);
        -webkit-animation-timing-function: cubic-bezier(0,0.5,0.43, 0.995);
    }

    48.6596% {
        -webkit-transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale3d(0.99, 0.99, 1);
        -webkit-animation-timing-function: cubic-bezier(0,0.455,0.59, 1);
    }

    55.8433% {
        -webkit-transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale3d(1, 1, 1);
        -webkit-animation-timing-function: cubic-bezier(0,0.67,0.53, 1);
    }

    100% {
        -webkit-transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale3d(1, 1, 1);
        -webkit-animation-timing-function: ease;
    }
}

は下記のように変換されます。

@-webkit-keyframes ani-an-obj-1 {
    0% {
        -webkit-transform: translate(0px, 0px) rotate(1deg) scale(0.9, 0.9);
        -webkit-animation-timing-function: cubic-bezier(0,0.54,0.6, 1);
    }

    8.4703% {
        -webkit-transform: translate(0px, 0px) rotate(-2deg) scale(1.15, 1.15);
        -webkit-animation-timing-function: cubic-bezier(0,0.54,0.525, 1);
    }

    16.5062% {
        -webkit-transform: translate(0px, 0px) rotate(0deg) scale(0.95, 0.95);
        -webkit-animation-timing-function: cubic-bezier(0,0.475,0.575, 1);
    }

    23.9405% {
        -webkit-transform: translate(0px, 0px) rotate(2deg) scale(1.05, 1.05);
        -webkit-animation-timing-function: cubic-bezier(0,0.5,0.705, 1);
    }

    32.7952% {
        -webkit-transform: translate(0px, 0px) rotate(0deg) scale(0.95, 0.95);
        -webkit-animation-timing-function: cubic-bezier(0,0.495,0.64, 1);
    }

    40.8074% {
        -webkit-transform: translate(0px, 0px) rotate(-1deg) scale(1.02, 1.02);
        -webkit-animation-timing-function: cubic-bezier(0,0.5,0.43, 0.995);
    }

    48.6596% {
        -webkit-transform: translate(0px, 0px) rotate(0deg) scale(0.99, 0.99);
        -webkit-animation-timing-function: cubic-bezier(0,0.455,0.59, 1);
    }

    55.8433% {
        -webkit-transform: translate(0px, 0px) rotate(0deg) scale(1, 1);
        -webkit-animation-timing-function: cubic-bezier(0,0.67,0.53, 1);
    }

    100% {
        -webkit-transform: translate(0px, 0px) rotate(0deg) scale(1, 1);
        -webkit-animation-timing-function: ease;
    }
}

やっていることは3d系の移動・回転処理を2d系に変えているだけです。
なので3d表現を可能にするものではありません。

beforeのtextareaにsenchaが書き出すhtmlソースをまるごとコピペすると、
afterにアニメーションcssのみ取り出してformatしたものが出力されます。

将来的にsenchaの方で対応されるまでの応急処置です。

webstorm tips

目録

  1. 使用メモリを増やす
  2. 外部ライブラリの追加
  3. キャッシュのクリア


使用メモリを増やす


たくさんのファイルを開いていると動作が重くなってしまう事があります。
その際、使用メモリを増やすと動作が軽くなる事があるので、マシンのメモリに余裕があるなら試してみましょう。

スクリーンショット 2012-10-27 7.16.02
右クリックから<パッケージの内容を表示>を選択。

スクリーンショット 2012-10-27 7.19.36
Contents/info.plistを開きます。

中には設定がxml形式で書かれています。
ファイルの最後の方にメモリに関する設定があり、初期状態は下のようになっています。

VMOptions.x86_64
      -Xms128m -Xmx800m -XX:MaxPermSize=350m -XX:ReservedCodeCacheSize=64m -XX:+UseCodeCacheFlushing -XX:+UseCompressedOops

ここの-Xmx、-XX:MaxPermSize、-XX:ReservedCodeCacheSizeの値を調整します。
ここでは2倍に設定し、

VMOptions.x86_64
      -Xms128m -Xmx1600m -XX:MaxPermSize=700m -XX:ReservedCodeCacheSize=128m -XX:+UseCodeCacheFlushing -XX:+UseCompressedOops

としました。
inclement

メモリの開放もできて、
右下のメモリ表示されている箇所をクリックすると開放されます。

memory

外部ライブラリを追加する

⌘ + .を押し、settingsを開きます。
Javascript – Librariesを選択し、右側の【Add…】ボタンを押します。

add

例としてGoogle Closure Library
を追加します。
あらかじめclosure libraryはローカルにダウンロードしておきます。
clo
ライブラリ名を入力し、【Attach】ボタンを押した後、ダウンロードしたディレクトリを選択します。

clo1
setting画面に戻ると、closure libraryが増えているので、チェックを入れて【OK】ボタンを押します。

clo3
エディタに戻ると、External Libararyにclosureが増えているのが確認できるでしょう。


キャッシュのクリア


たまにソフトが立ち上がらなくなったり、動作が不安定になったりすることがあります。
その時はキャッシュをクリアしてみると改善される事があります。

※ただしhistory(ファイルの変更履歴など)など全てクリアされるので注意。ショートカットや外観の設定はクリアされません。

macだと

~/Library/Caches/WebIde50

の中を全て削除し、再起動してみましょう。

WebStorm入門

The best JavaScript IDE with HTML Editor for Web development :: JetBrains WebStorm
ショートカットや機能の紹介をしていきます。

1.ソース解析偏

重要度★★★★★ (必須)

補完

Control + spcale

定義箇所にジャンプ

hoge.method()と書いてある箇所のmethodの部分を
⌘ + クリック
または、
⌘ + b

ファイル名で検索

⌘ + Shift + n
スクリーンショット 2012-10-22 1.46.54

変数名で検索

⌘ + Option +Shift + n
スクリーンショット 2012-10-27 7.03.25

フォルダから検索

フォルダを右クリック → Find in path
または Control + Shift + f

重要度★★★★ (重要)

戻る/進む

⌘ + ⌥ + ← (戻る)、⌘ + ⌥ + → (進む)

⌘ + click等で定義元に飛んだ後、元の場所に戻る。

定義箇所をクイックプレビュー

⌘ + Shift + i
スクリーンショット 2012-10-22 1.56.11

⌘ + クリックだとそのファイルに表示が移ってしまうが、
これだと該当箇所がポップアップでプレビューされる。

スーパーメソッドにjump

⌘ + U

関数・変数の使用箇所を探す

option + F7
または
右クリックから【Find Usaages…】
スクリーンショット 2012-10-28 11.20.04

f7

クラス構造を表示

Window左すみにある、Structureをクリック。
str

または、
⌘ + F12 (ポップアップ)

重要度★★★ (便利)

直近まで編集していた箇所に戻る

⌘ + Shift + Delete

手元が狂って妙な箇所に画面がフォーカスしてしまった際に便利

行数指定をして移動

⌘ + g

2.コーディング編

live template

⌘ + . でsettigを開く
live


IDE Settings → Live Templates
右画面のjavascriptを選択。


右の + ボタンを押す。


下画面、Abbreviation にはトリガーとなる文字を入れる。
ここではco


Template textには生成される文字列を入れる。
変数も使える。ここでは $END$ を入れている。

console.log($END$);


下にある青い「Define」の文字を押す。
Javascriptにチェックを入れる。

右下のApplyを押して決定。

以後、「co」と入力してからTabを押せば、
console.log(); と自動で挿入され、
$END$の場所にカーソルが移動するようになる。

1行コメントアウト

⌘ + Shift + /

複数行コメントアウト

Control + shift + /

重要度★

ブックマーク

F11 でブックマークに追加。
Shift + F11 で一覧を見る
bookmark

良くいじる箇所はブックマークしておくと便利。

3.SVN

スクリーンショット 2012-10-22 0.41.24

メニューの VCS → Enable Version Control Integration…
を押し、
スクリーンショット 2012-10-22 1.04.29
メニューからSbuversionを選択。

4.その他

スキン変更

⌘ + . でSettingsを開く。
skin
Color & Fonts を選択。
Scheme nameで選び、Applyを押すと反映される。

Home

Return to page top