非同期のコールバック関数に外から値を渡す
コールバック関数のパラメータ以外のものを外から渡す方法について。
ここでは setTimeout()
の第1引数に渡すコールバック関数を例にします。
単に主題の件をやりたければ、コールバック関数のスコープ外で宣言した変数に渡したい値をセットしておいて、それをコールバック関数の内側から参照させればいいです。
例として次のコードを実行すると、1000ミリ秒後に alert
で『(^-^)』が表示されます。この場合、str
がコールバック関数のスコープ外の変数にあたります。
ただしこの方法で参照される str
の値は、実際にコールバック関数が実行された時点における str
の値となります。
それを確認できるのが次の例です。次のコードを実行すると、1000ミリ秒後に alert()
で表示されるのは『(^-^)』ではなく『(>_<)』です。
setTimeout()
はnミリ秒後に行う処理を予約する関数です。setTimeout()
で alert()
を予約した後、1000ミリ秒後までに str
の中身が変化してるので、このようになります。
もし「コールバック関数を仕掛けたタイミングにおける変数の値」を実行時にも使用したい場合は、次のように書きます。次のコードを実行すると、1000ミリ秒後も alert
で『(^-^)』が表示されます。
これは、「『alert()
を行う無名関数』を生成して返却する別の関数 (ここでは createCallback()
)」を間に挟んでる形です。
JavaScriptの引数は、Object型ではない限り値渡し = 『値がコピーされた別の変数』扱いです。createCallback()
で生成した無名関数が参照している arg
は、あくまで createCallback()
が実行されたときに渡された str
のコピーなので、こういうことができます。
余談
なんでこんな記事を書いたのかというと、初心者の頃にループ内で非同期のコールバック関数に外からループカウンタを渡して、想定通りに動かなかったことがあるからです。
例として次のコードを実行すると、1000ミリ秒後に alert()
で 3
が3回表示されます。実際に setTimeout()
で仕掛けたコールバックが実行されるタイミングでは、すでに i
の値がループを走り抜けて 3
に変化しているためです。
なお、このケースに限っては var
の代わりに let
を使うだけで解決します。let
ならばループごとに別スコープ扱いになるので、同じように書いても1000ミリ秒後に alert()
で 0
-> 1
-> 2
と表示されます。