私の場合、JavaScriptの学び始めに面食らうようなことがいろいろとありました。その1つが再帰っぽい非同期処理です。再帰っぽいと言っているのは、それは再帰処理ではないのにパッと見再帰的に見えてしまうのでそう言っております。
追記:再帰は再帰、非同期は非同期、再帰を非同期実行するだけのことです。両立しないことではないのにおかしなことを書いてしまいました。なので、タイトルからしておかしいですね。申し訳ございません。
全く実践的ではなく、こんなバカな書き方をすることはないと思うのですが、入力値をカウントアップする単純な例で。まずは再帰。
<body> <input type="number" name="number" value="0" /> <button>実行</button> <div id="result"></div> <script> window.onload = function() { document.querySelector('button').onclick = function() { var result = (function countTo(n, i) { if (i >= n) return i; return countTo(n, i + 1); })(parseInt(document.querySelector('[name="number"]').value), 1); document.getElementById('result').appendChild(document.createTextNode(result)); document.getElementById('result').appendChild(document.createElement('br')); }; }; </script> </body>
例えばChromeで100000なんて数値を入力して実行すると、あっさりスタックオーバーフローします。次は再帰っぽい非同期処理でカウントアップする例(いや、本当に実行しないでくださいね。恐ろしく時間がかかりますから)。
<body> <input type="number" name="number" value="0" /> <button>実行</button> <div id="result"></div> <script> window.onload = function() { document.querySelector('button').onclick = function() { (function (n, i, done) { function countTo(i) { if (i >= n) done(i); else setTimeout(function() { countTo(i + 1) }, 0);; } setTimeout(function() { countTo(i + 1); }, 0); })(parseInt(document.querySelector('[name="number"]').value), 1, function(result) { document.getElementById('result').appendChild(document.createTextNode(result)); document.getElementById('result').appendChild(document.createElement('br')); }); }; }; </script> </body>
当たり前ですが、こちらはスタックオーバーフローしません。慣れてしまえばなんてことはないのですが(setTimeoutしているのだから一目瞭然ですね)、JavaScriptからしばらく離れていると頭はついついcountToからcountToをコールしている..と読んでしまいます。いやとんだ無駄話を、失礼しました。