関数とプロミスを使用したループ
解決済
回答 1
投稿
- 評価
- クリップ 0
- VIEW 368
関数とプロミスを使用して、ループ処理を行おうとしています。
非同期のループを実現させたく、プロミスを使用した関数のループを実現しようとしていますが、条件処理でresolve
しません。
<!DOCTYPE HTML>
<html>
<head>
<script src="lib/jquery-2.2.0.min.js"></script>
<script src="js/test.js"></script>
</head>
<body>
<button type="button" onclick="xxx()">クリック</button>
</body>
</html>
<test.js>
let Looptest = function() {
this.num = 0;
}
Looptest.prototype = {
aaa: function() {
let defer = $.Deferred();
setTimeout(function() {
console.log('setTimeout')
defer.resolve();
}, 500);
return defer.promise();
},
bbb: function() {
let self = this;
let defer = $.Deferred();
let promise = self.aaa();
promise.done(function() {
// 5に達したら、resolveさせたい
self.num = self.num + 1;
if (self.num < 5) {
console.log(self.num)
self.bbb();
}
else {
defer.resolve();
}
});
return defer.promise();
}
}
function xxx() {
let lt = new Looptest();
let promise = lt.bbb();
promise.done(function() {
console.log('success!');
})
}
発生している問題・エラーメッセージ
function xxx()
を実行させると、以下のように途中までのコンソールログは表示されるのですが、最終的ゴールの「success!」が表示されません。
setTimeout
1
setTimeout
2
setTimeout
3
setTimeout
4
setTimeout
試したこと
bbb: function()
内のself.num = self.num + 1;
の位置を変えたりしました。
補足情報
function bbb()
内の条件分岐でelse
を設定しないと、「success!」は表示されるのですが、思ったような順番?に表示されません。
bbb: function() {
let self = this;
let defer = $.Deferred();
let promise = self.aaa();
promise.done(function() {
self.num = self.num + 1;
// 5に達したら、resolveさせたい
if (self.num < 5) {
console.log(self.num)
self.bbb();
}
defer.resolve();
});
return defer.promise();
}
setTimeout
1
success!
setTimeout
2
setTimeout
3
setTimeout
4
setTimeout
質問
どのようにすれば正確にresolveして「success!」まで辿り着けるのか、なるべくコードを変えずに、ご指摘をいただけると助かります。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+2
なるべくコードを変えずに
いい縛りですね! 燃えますね!
コードを変えた度合いの測定はレーベンシュタイン距離とかでしょうか?
当方は56でした!
let Looptest = function() {
this.num = 0;
}
Looptest.prototype = {
aaa: function() {
let defer = $.Deferred();
setTimeout(function() {
console.log('setTimeout')
defer.resolve();
}, 500);
return defer.promise();
},
bbb: function(defer) { // 変更
let self = this;
let promise = self.aaa();
promise.done(function() {
// 5に達したら、resolveさせたい
self.num = self.num + 1;
if (self.num < 5) {
console.log(self.num)
self.bbb(defer); // 変更
}
else {
defer.resolve();
}
});
return defer.promise();
}
}
function xxx() {
let lt = new Looptest();
let promise = lt.bbb( $.Deferred() ); // 変更
promise.done(function() {
console.log('success!');
})
}
追記
↓これでレーベンシュタイン距離を23にすることができました!
let Looptest = function() {
this.num = 0;
}
Looptest.prototype = {
aaa: function() {
let defer = $.Deferred();
setTimeout(function() {
console.log('setTimeout')
defer.resolve();
}, 500);
return defer.promise();
},
bbb: function(defer) {
let self = this;
letdefer = $.Deferred();
let promise = self.aaa();
promise.done(function() {
// 5に達したら、resolveさせたい
self.num = self.num + 1;
if (self.num < 5) {
console.log(self.num)
self.bbb(defer);
}
else {
defer.resolve();
}
});
return defer.promise();
}
}
function xxx() {
let lt = new Looptest();
let promise = lt.bbb($.Deferred());
promise.done(function() {
console.log('success!');
})
}
(ネタです)
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.22%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2019/09/12 20:10 編集
さて、Lhankor_Mhyさんのとおり修正したのですが、コンソールログが、「補足情報」に載せたような「success!」が3行目に表示されてしまいました。これは、こういう挙動なのでしょうか?
まだ、ほかに聞きたいことがありますが、とりあえず、ここでは上記の点についてお伺いしたいと思います。
2019/09/12 20:19
当方とは結果が異なるようです。
https://jsfiddle.net/Lhankor_Mhy/4cdsxrwp/
tara-tailさんの環境では、↑のコードを実行すると、『「success!」が3行目に表示され』ますか?
表示されるのであれば、同じコードで当方と違う結果なので、環境の問題かと思います。
表示されないのであれば、当方と同じ結果なので、ご提示いただいていない部分に原因があるのかもしれません。
2019/09/12 20:31
それで、一つ聞きたいのは、やはり引数の(defer)です。で、bbb()のほうでは、「let defer = $.Deferred();」を削除されていますのね。これは「aaa()」の方で定義されているから、余計だったのでしょうか?これの仕組みを教えてください、というのはちょっと乱暴ですので、なにかヒントを教えていただければ助かります。なぜ、引数がなかったら、うまくいかなかったのでしょう?「こういうものだ」と言ってくれても構いません。よろしくお願いします。
2019/09/12 20:40
self.num == 1
の時ですが、
if文に入り条件不成立で
self.bbb();
を呼び、
defer.promise()
を返しますよね。
そのあと、
self.num == 5
の時ですが、
if文に入り条件成立で
defer.resolve();
でプロミスを解決させるわけですが、この時の defer は let defer = $.Deferred(); ですから、この関数呼び出しに限ったスコープです。
なので、self.num == 1 の時に返した defer.promise() の defer とは別のものである、ということです。
これは想定と異なるのではないですか?
2019/09/13 08:31
aaa()もbbb()も同じ「let defer = $.Deferred();」にまとめた方が良い、という認識でよろしいでしょうか?
条件式のifとelseで同じdeferが返されていないという原因がわかりました。
ifの時は「aaa()」の「defer」を呼び出していて、elseの時は「bbb()」のdeferを呼び出している、という認識しています。
これは、全く想定していなかったことです。
同じにするには、引数を使用して,bbb()では「 let defer = $.Deferred(); 」を宣言しない(書かない)ということですよね?
または、コンストラクタで、「this.defer = $.Deferred();」とした方が良いのでしょうか?
もっと良い簡潔なアイデアがございましたら、教えてくいただけないでしょうか?
2019/09/13 10:07
うーん、どうでしょうか。そうすると defer のチェーンを切らないようにしないといけないですよね。
bbb() が aaa.done() の戻り値を返さなきゃいけないけど、bbb() は再帰するから……、かえって混乱しそうな……?
> または、コンストラクタで、「this.defer = $.Deferred();」とした方が良いのでしょうか?
それはわかりやすいと思います。
> もっと良い簡潔なアイデアがございましたら、教えてくいただけないでしょうか?
async await で書いたほうが楽だと思います。前の質問を拝見するとトランスパイラをお使いなのでしょうから、使わない理由はないと思います。
2019/09/13 21:25
最初にご回答していただいたものを元に、コンストラクタでDeferredを定義してみたり、いろいろ試してみたいと思います。まずは、ご回答のとおりにすればうまくいくので安心できました。また、よろしくお願いします。