はじめに
今更ではありますが、諸事情があってjQueryを使うことになったので自分なりのメモの
意味も兼ねて
非同期処理と順番
jQueryを使用する理由の一つにajaxによる非同期処理が可能になると言う点があります。
しかし、今まで基本的に科学計算しかしてこなかった人間からすると、
プログラムが上から順番に流れずに実行されることに違和感を感じてしまっていました。
どういうことかと言うと、Cなんかで数値計算を書いた場合基本的に先に書いてある文が実行されてから次の文に処理が移動します。
しかし、ajaxを使用するとajx通信を行なっている部分は完了していようがいなかろうが、関係なしに次の文に移ってしまうことになります(それが良いところでもあるのですが…)。
ajax通信をしてサーバー側から値を受け取ったものを表示したい場合などには、
ajax通信が完了してから値の表示を実行する必要があります。
そこで登場するのがコールバック関数になります。
コールバック地獄とその対策
コールバック関数とは以下のようなものを指し、ある関数Aに関数Bを引数として与えて、関数Bを関数A内で実行するものになります。
test(call);
function test(callback_function) {
console.log("Hi!");
callback();
}
function call(){
console.log("I'm called")
}
//実行結果
//Hi!
//I'm called
次に非同期処理についてです。下のプログラムはsetTimeoutを使用して、非同期処理を作っています。
var a = 1;
console.log("first",a);
setTimeout(function(a){a = 100},2000);//2秒待ってからaの値を書き換える
console.log("second",a);
//実行結果
//first 1
//second 1
setTimeoutはプログラムの流れとは関係なしにとりあえず2秒間待つので
当然secondで表示したaは書き換えが終わっていないので元の値(つまり1)のままです。
これの解説案として一番単純なのは下のように書いてしまうことです。
var a = 1;
console.log("first",a);
setTimeout(function(a){
a = 100;
console.log("second",a},2000);//2秒待ってからaの値を書き換えて出力する
//実行結果
//first 1
//second 100
このようにsetTimeout配下にconsole.logを配置してしまえば、
setTimeout配下では同期処理ですので通常通り先に書いたプログラムが先に実行されます。
なんだか科学計算の並列計算と似てるなぁと私は思いました。
コールバック関数使ってなくね?と思われる方もいるかもしれませんが、setTimeout内に書かれている
無名関数が今回のコールバック関数ということになります。
これで問題解決のように思えるかもしれませんが、今回のaの書き換えの処理のさらに1秒後にaを1000に更新したいとします。そうするとコードは以下のようになります。
var a = 1;
console.log("first",a);
setTimeout(function(a){
a = 100;
console.log("second",a}
setTimeout(function(a){
a = 1000;
console.log("3rd",a}
,1000)
,2000);//2秒待ってからaの値を書き換えて出力する
//実行結果
//first 1
//second 100
//3rd 1000
段々とコールバック地獄の片鱗が見えてきました。
このように非同期処理内でさらに非同期の処理を行おうとすると
延々とネストが深くなる上に正直なにをやっているのか分からなくなります。
これの解消策として次に紹介するpromiseになります。
promiseとarguments
コールバック地獄を解消するための仕組みがpromiseです。
以下のように書きます
//jQUeryを使います
var a = 1;
console.log("first",a);
$.when(change_value(a))
.done(console.log("second",arguments[0])
function change_value(x){
var dfd = new $.Deferred();//defferedオブジェクトのインスタンス化
setTimeout(function(x){x = 200;
dfd.resolve(x);
},2000);//2秒待ってからaの値を書き換える
return dfd.promise()
}
//実行結果
//first 1
//second 200
promiseは非同期の処理が終わったタイミングで信号を送ってくれるような仕組みです。
そのため、非同期処理が完了した後に処理を行うことができます。
さてここからが本題となります。
promiseで信号を受け取り、非同期処理によって更新もしくは得た値を使いたいとします。
その値はarguments(MDNを参照: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/arguments)
に入っているのですが、
このリファレンスが少々曲者です。というのも上記のリファレンスによると
-----引用-------
arguments は、関数へ渡された引数を含む、関数内のみアクセス可能な 配列様 (Array-like) オブジェクトです。
----引用終了----
これだけを見るとargumentsには関数内のみアクセス可能な配列オブジェクトである可能性があります。
つまり
arguments = 関数へ渡された引数
なのか
argumentsには関数へ渡された引数の他にも何か入っている
のかが分かりません。
これについて原文の英語の方を当たるとすんなり答えが見つかりました。
日本語で「含む」と訳されている部分がincludeではなくcontainでしたので
この場合は
arguments = 関数へ渡された引数
という認識が正しいはずです。