#jquery

前記:

この記事では多数のAJAX通信が発生した際、 「非同期」の処理順番を保障する方法を検討します。 すべては私個人の経験なので、もしこれよりもっどいい方法があれば ぜひ教えてください。 実際この問題にぶつかった際の考えの流れで書いています。 使えない案や、懸念がある案も全部述べたので、文章が長くなってしまいました。 開発する当時はまだインターネットにつながらないので、 何の情報も取得できず、考えたんです。 後でネットで調べたらAjax Queueのプラグインが 同じことをしてくれます。 このプラグインを使った後に所感を書く予定です。

本題

AJAXはご存知だと思います。非同期通信でページをリフレッシュせずに動的に通信ができる、 リッチなウェブアプリケーションを構築する上で不欠けんな存在であります。

一方、この「非同期」というのは特徴であり、たまには厄介なことになります。 例えばajaxの処理と普通の処理の実行順序を気にするとき、 コードの書いた順番とおり実行する保障はありません。

jQueryでコードを書くと、こんな感じです。

$.ajax(option);
alert('ajax done');

ここでajax通信が終わってからalertが出るように見えますがそうとは言い切れないです。 ajax通信が終わってない段階でalertが出るかもです。 ならばどうしたらいいんですか?

一番簡単なのはjQueryが用意したコールバック関数です。

$.load(url, [data], [callback])
$.get(url, [data], [callback], [returnType])
$.getJSON(url, [data], [callback])

などがあります。 [data]がなければ、コールバックの関数を二番目のパラメータで書いてもOKです。 jQueryは内部的に判断してくれます。dataなのか、コールバックかを。

そして$.ajaxにはoptionでsuccessやcompleteにコールバックを設定できます。 これらを用いて上記のコードを以下のように改善できます。

$.ajax(option)の場合

.load(url,[data],[callback])
$.get(url,[data],[callback],[returnType])
$.getJSON(url,[data],[callback])

ここで問題2に入ります。

もし必要なajax通信が一つ以上で、すべてのajax通信が完了した後に何かの処理が実行したい場合は どうすればいいでしょうか。

コードで書くと、こんな感じです。

$.ajax(option1);
$.ajax(option2);
$.ajax(option3);
function(){
	alert('all ajax done');
}

案1:ajaxのグローバル関数

jQueryではajaxのグローバル関数と呼ばれるいくつかの関数を提供してくれました。 それらは以下のようなものです。

.ajaxComplete(handler)
.ajaxSuccess(handler)

これらはDOM上のどの要素にも設定でき、AJAX通信が行われる際に呼び出されます。

例えば以下のようなコードがあります。

$(document).ajaxComplete(function(){
	alert('ajax complete');
});

これはdocument要素において、何かのajax通信が完了したらパラメータのコールバックを実行する意味です。 selectorにはもっと範囲を縮むことができます。 例えばこんなHTMLがあるとします。

<div id='content'></div>

$('#content').ajaxComplete(function(){
	alert('content ajax complete');
});

これならidがcontentのdivにajax通信が完了する際のコールバックを示しています。

しかしこれだけではまだ問題2を解決できません。

$.ajax(option1);
$.ajax(option2);
$.ajax(option3);
$(document).ajaxComplete(function(){
	alert('ajax complete');
});

こう書くと、任意の通信が完了したらこのajaxCompleteが呼ばれ、 ほかの二つの処理がどうなってるかはまったくわからないです。

ここで$.ajax(option)のoptionに注目しましょう。 optionにはglobalというフラグがあり、デフォルトはtrueになってます。 その役割はグローバル関数の監視対象にするかしないかです。 このフラグをfalseに設定すれば、該当のajax通信がどうなってもグローバル関数はトリガーしません。 なので、通信処理1と2のglobalフラグをfalseに設定し、最後の処理3をtrueに設定すれば、 処理3が実行完了した際、グローバルのajaxCompleteが実行されます。

改善したコードは以下になります。一部省略しました。

var option1 = {
	global: false
	// ほかのurlなどの設定
}

var option2 = {
	global: false
	// ほかのurlなどの設定
}

var option3 = {
	global: true
	// ほかのurlなどの設定
}

$.ajax(option1);
$.ajax(option2);
$.ajax(option3);
$(document).ajaxComplete(function(){
	alert('ajax complete');
});

しかし、ここで懸念事項があります。 ajax処理の間の順番はどうなるか断言できません。 つまりこういった順番で実行されたかもです。

処理1実行 処理1完了 処理2実行 処理3実行 処理3完了 グローバルの関すを呼び出す 処理2完了

軽く動作確認ではちゃんと思ったとおり1,2,3で動いていますが、 やはりその辺が気になって、やめました。

案2:ajaxをラッパーした関数を作る

$.ajax(option)のoptionにはcompleteというのがあります。 successと使い方は同じで、successは通信が成功した際実行するコールバックで、 completeは完了した際のコールバックです。

なので今回思い出したのアイディアはこのcompleteに次のajax処理を指定して、 さらにその次のajaxのcomplete optionにも後のajax処理を指定したらどうでしょうか。 尻尾をどんどん噛んでいく蛇のイメージです。

(抜粋)

var option1 = {
	complete: $.ajax(option2)
}

var option2 = {
	complete: $.ajax(option3)
}

var option3 = {
	complete: function() {
		alert('ajax all complete');
	}
}

$.ajax(option1);

動作確認でOKでした。 completeオプションで前のajax通信が必ず完了した後に次のajax通信を始まることを保障しています。 これでajaxのチェインができました。

これをちょっと綺麗にラッパーした関数を作りました。 ご覧ください。

function doOrderGuaranteedAjax(ajaxOptionArray, allCompleteHandler){
  var defaults = {
    type : "GET",
    dataType : "text",
    complete : function() {
      // 最初の要素を削除
      ajaxOptionArray.shift();
      // すべての通信が完了した場合
      if (ajaxOptionArray.length == 0 ) {
        // コールバックが設定された場合
        if (allCompleteHandler) {
          allCompleteHandler();
        }
      } else {
        // 通信配列にまだ通信が残っている場合
        option = ajaxOptionArray[0];
        // ajaxのオプションを次の通信に切り替え
        opts = $.extend({}, defaults, option);
        // 通信を開始
        $.ajax(opts);
      };
    }
  };

  // 初期指定
  var option = ajaxOptionArray[0];
  var opts = $.extend({}, defaults, option);
  // 一回のみ実行
  $.ajax(opts);
};

まず$.ajax(option)のoptionを順番でpushした配列が第一パラメータで渡されます。 2~23行まではデフォルトのoptionを構築し、一回目の処理は29行から走ります。 そしてデフォルトのoptionではcompleteで実行完了した処理を配列から削除(7行)します。 配列にまだ待ち状態の処理があるならオプションを次の通信に切り替えます(14~20行)。 もし配列にある処理が全部実行完了したら、コールバックを呼びます(9~13行)。

使い方はまずオプションを一つの配列にpushし、その配列を一番目のパラメータで、 コールバックを二番目のコールバックで渡します。

var optionArray = [];
optionArray.push(option1);
optionArray.push(option2);
optionArray.push(option3);
doOrderGuaranteedAjax(optionArray, function(){
	alert('all complete');
});

arrayのpushとshiftを利用し、スタック構造を真似してます。 push() は array の最後に配列要素を加えます。 shift() は array の最初の要素を削除します。

これでファーストイン、ファーストアウトFIFOが実現し、 処理の順番が保ちます。

#iphone

</param></param></param></embed>

Google Waveはみんなご存知だと思います。

5月発表以来今だテストの最中です。

wave.google.comでテストの申請を出しているのですが、

全然返事がこないです。。。


さて、Google WaveはiPhoneでも使えるのを期待する人はたくさんいるでしょう。

最近そのような記事も増えてきた一方

Googleからは特に詳しい情報はあまりないですね。

私も妄想してる最中、このYoutubeの動画を見つかりました。

なんとこれはMacのKeynote(WindowsにおいてはPowerPoint見たいなソフト)で作ったらしい。

iPhoneにどんなwaveが登場するかお楽しみですね。


#webservice

今回紹介するのは読書メーターです。
簡単にいうと、読書を記録、管理するサイトです。

f:id:kinopyo:20091026235301j:image



FC2のブログアプリケーションにも似た感じの読書カレンダーがありますが、
比較してみて、やはり読書メーターのほうが機能も充実し、面白いと思います。

まずユーザ登録はFC2や、はてな、そしてmixiのIDでそのままログインできます。
そして自分が読んだ本をどんどん登録すると、面白いグラフができてますよ。

本の追加は以下の4種類あり、自分が今までの「実績」がすぐわかります。

  • 読んだ本に追加
  • 読んでる本に追加
  • 積読本に追加 ※持っているけど読んでいない本など
  • 読みたい本に追加 ※持っていない読みたい本など

コメントはもちろん入力でき、さらに本を読んでる、読んだ人の人数まで数えてくれます。
本の追加、コメントする時は自動でTwitterにも投稿できます。

本はページ数まで把握して、グラフで自分がどれほどのページを読み終わったかも見れます。
私はログインしてから一気に全部の本を追加しました。

ほかにも読友、相性、職本、お気に入り、コミュニティ、ブログパーツなどの
機能が充実してます。
相性や職本などのジャンルはなかなかアイディアのいい機能じゃないですか。
ぜひ試してみてください。

#diary

インストール先のCドライブをあっさりフォーマットしてから

Windows 7をインストールしました。

さすがVistaの失敗からやりなおしたマイクロソフト、

今回の新作は何年ぶりの傑作だと思います。


インストールは特に問題なし、そのまま進みました。

完了したら、まずタスクバーの変更に目がつきました。

スクリーンショットをご覧ください。


進化1

一つのアプリケーションが一つのアイコンになる。

一つのアプリケーションでいくつかのウィンドウを開いても

タスクバーではやはり一つのアイコンに所属され、

表示が何枚か重なるようになる。


f:id:kinopyo:20091031200524j:image

この例で言うと、フォルダーが二つ、Firefoxのウィンドウが三つ開いてます。


進化2

アイコンにマウスオーバーすると、 そのアプリケーションで開かれているウィンドウの数分の略図が表示される<


f:id:kinopyo:20091025230509j:image


進化3


f:id:kinopyo:20091025233902j:image

さらにその略図にマウスオーバーすると、その実ウィンドウが表示される


まだインストールして2時間もたってませんけれど、

Windows7の使いやすさを感じました。

Vistaは一回仕事で触ったことがありますが、

ひどいもんですよね。

なのでずっとXPを使ってきました。

今日Windows7にアップグレードして驚きました。

皆さんもぜひ触ってみてください。

#design

たまたまこういうテーマの記事を読みました。

とても感動しました。

3年前の記事ですが、未だ見ても全く言う通りです。

ウェブアプリケーションのインターアクションをどのように設計すべきかを議論する記事で、

今ウェブ上存在するユーザにとって「不親切」な例をどんどんあげて説明してくれました。

以下抜粋した内容です。

インタラクションデザイナーはコモンケースを重視すべきなのです。起こりそうもない場合を考慮し過ぎて、起こるべきことの利便性を低下させてはいけません。

例えば、多くのジュースの自動販売機では、1000円札を入れた場合に、ボタンを押して商品を1つ購入すると「続けて購入モード」に入ってしまうので、いちいち釣り銭レバーを押さないといけません。しかし(おそらく)ほとんどのケースでは、ユーザーは一度に1本しかジュースを購入しないのです。つまり釣り銭レバーを押すという余計な操作が膨大な回数行われていることになります。

プログラマーはエッジケースを重視しますが、インタラクションデザイナーはコモンケースを重視すべきなのです。起こりそうもない場合を考慮し過ぎて、起こるべきことの利便性を低下させてはいけません。

こういうケースはウェブ上にたくさん存在していると思います。

自販機の例では本当に身のまわりの話で、誰もが想像できるんだろう。

私の場合、今までは全部1本購入してきました。

1000円札入れる旅、釣り銭レバーを押さなければなりませんのは面倒の中の面倒です。


さらにもう一つの事例として、ユーザ登録時のメールアドレス入力についても指摘がありました。

これはEメールアドレスを正確に入力させることを目的としていますが、ユーザーの行動を観察すると、非常に多くの場合、ユーザーは1つ目に入力した内容をコピーして2つ目にペーストしています。これでは単に手間が増えているだけ。

 また、ほとんどの場合システムは、2つの値が一致しているかどうかをバリデートするだけで、そのアドレスが有効かどうかを調べているわけではありません。もし1つ目のフィールドに正しく入力できても、2つ目のフィールドで間違えればエラーになってしまいますし、逆の場合もエラーになります。人は正確な作業を繰り返すことが苦手ですから、かえってエラーを増やしていることになります。仮にユーザーが90%の確率で自分のアドレスを正しく入力できるとした場合、

・フィールドが1つの場合、90%の確率で正しく登録できる

・フィールドが2つの場合、81%の確率で正しく登録できる

という計算ができ、フィールドを増やすことでかえって「登録する」という目的を達成するハードルが高くなってしまうといえるのです。

私の場合はいつも最初の一個目が絶対100%正しく登録してます。

(いつも保存された記録があり、Tabキーで選択するのがほとんどでした)

しかし、二つ目で誤って入力したケースは何回かありました。

確かに確率が下がってると思います。

ユーザインタフェース設計する時は、ぜひこの記事をご覧ください。

絶対役に立ちます。

#movie
  • 日時: 09年10月24日
  • 場所: シネマサンシャイン池袋
  • おすすめ度: ゼロ、絶対見ないでください
  • コメント:

こんなに気持ち悪い映画は生まれて初めて。

神、あの世などのテーマのアニメや漫画、映画も今までたくさん見てきましたが、

これほどひどいのはないだろう。

ちゃんと科学を信じて、考えてる人に対して今までの人生が無駄だと言うのは何様のつもり?

あの世のことを知らなければ成功できないとか、

何が幸せ、何を求めてるとか、今から反省するほど俺らの人生はそんなに失敗していない!

お説教はやめてほしい。見苦しいです。

実際は1分間の予告編ほどすばらしくないです。

映画の途中でも次々と退場した人がたくさんいました。

「これは何なんだ」、「これはないでしょう」との声もありました。

人生最も無駄の2時間でした。

#javascript

javaをやってからJavaScriptにかえると、なかなかなれませんね。

例えばJavaではオーバーロードができるが、JavaScriptには通用しない。

すごく簡単に言うと、オーバーロードは多重定義の意味で、同じメソッド名で違う型、数のパラメータがある時、それぞれ一つのメソッドとして認められる。 一方、JavaScriptでは後勝ちです。先に定義したものが無効になります。

function func(param) {
  alert(param);
}

function func() {
  alert(1);
};

func('test');
func();

同じfuncの名前で作られた関数で、一個目はparamというパラメータがついてる。 二番目はパラメータがない。すると結果は: 1 1 になります。常に後で定義したものが勝ちます。

function func() {
  alert(1);
};

function func(param) {
  alert(param);
}

これは「test undefined」になります。