あなたはfor
ループ内で非同期操作の束を開始し、for
ループが実行されたときに行われるすべての非同期操作を期待しています。しかし、現実には、あなたのdoc.comments
配列がまだ入力されていないように、それらのどれもまだ実行されていません。あなたがそれを使用しようとしていた場所で定義されているように、それが人口になっていた前にそれを使用しようとしていました。
これを解決する最良の方法は、Promisesを使用して、BlubirdのPromise.map()
またはES6 Promise.all()
などの複数のリクエストを発行し、すべてのリクエストが完了したときに約束のエンジンが通知する方法を学ぶことです。
データベースを変換するショートが約束を使用する以上呼び出して、あなたは手のコードをすることができ、次のようにすべてが行われたときに知っている:
ハンドコーディングされたコールバックの実装
router.get('/:id', function (req, res, next) {
List.findOne({listurl: req.params.id}, function (err, doc) {
var doneCnt = 0;
if (!err && doc != null) {
for (var i = 0; i < doc.comments.length; i++) {
(function(index) {
User.findOne({Name: doc.comments[i].commenter}, function (err, data) {
++doneCnt;
if (err) {
// need some form of error handling here
doc.comments[index].gvUrl = "";
} else {
if (data) {
doc.comments[index].gvUrl = gravatar.url(data.Email, {s: '200', r: 'pg', d: '404'});
} else {
doc.comments[index].gvUrl = 'noGravs';
}
}
// if all requests are done now
if (doneCnt === doc.documents.length) {
res.render('list', {
appTitle: doc.Title,
list: doc
});
}
});
})(i);
}
}
else {
res.status(404).render('404', {appTitle: "Book not found"});
}
});
}
このコードは認識し
User.findOne()
非同期操作を実行していますループの中で。
- これらの非同期操作は、任意の順序で終了できます。
- これらの非同期操作は、ループが完了しても完了しません。すべてのループが完了すると、操作が開始されます。彼らはすべて後で不確定な時間を完了します。
- すべての非同期操作がいつ完了したかを知るために、開始された合計要求数にカウントが達したときに、完了したページ数をカウントするカウンタを保持します。これは、すべてが完了した時を知る方法に対する「手作業」方法です。
ブルーバードの約束の実現
は、ここではブルーバードを使用して仕事ができる方法ですライブラリを約束し、約束をサポートするようにデータベース操作を変換:
var Promise = require('bluebird');
// promisify the methods of the List and User objects
var List = Promise.promisifyAll(List);
var User = Promise.promisifyAll(User);
router.get('/:id', function (req, res, next) {
List.findOneAsync({listurl: req.params.id}).then(function(doc) {
if (!doc) {
throw "Empty Document";
}
return Promise.map(doc.comments, function(item, index, length) {
return User.findOneAsync({Name: item.commenter}).then(function(data) {
if (data) {
item.gvUrl = gravatar.url(data.Email, {s: '200', r: 'pg', d: '404'});
} else {
item.gvUrl = 'noGravs';
}
});
}).then(function() {
return doc;
});
}).then(function(doc) {
res.render('list', {
appTitle: doc.Title,
list: doc
});
}).catch(function(err) {
res.status(404).render('404', {appTitle: "Book not found"});
});
}
はここで、これはどのように動作するかです:
- Bluebird約束ライブラリをロードする
- 約束したメソッドを
User
とList
オブジェクトに追加して、約束を返す新しいメソッドを追加します。
List.findOneAsync()
と呼んでください。メソッド名の"Async"
接尾辞は、.promisifyAll()
が追加した新しいメソッドを表します。
doc
がない場合、約束を拒否するスローは、最後に.catch()
で処理されます。プロミスは、非同期エラー処理のために非常に便利な非同期スローセーフです。
doc.comments
のPromise.map()
を呼び出します。これにより、doc.comments
配列が自動的に反復され、配列内の各項目のイテレータが呼び出されます(Array.prototype.map()
に似ていますが、イテレータから返されたすべての約束を集め、すべての根本的な約定が解決されたときに解決されます)。道、それはすべてのイテレータを並列に実行することができますし、すべてのイテレータが行われたときにわかります。
- イテレータは
User.findOneAsync()
を呼び出し、結果とdoc.comments[index].gvUrl
値を設定します。
Promise.map()
に余分.then()
ハンドラはちょうどにありますその約束の解決された値をdoc
オブジェクトに変更して、それを外側の約束ハンドラから得ることができます。
- 外側の約束から成功するために、レンダリングします。
- 外側の約束からのエラーについては、404ページを表示してください。このスキームのどこかで拒否された約束は、伝播してトップレベルで拒否されることを覚えておいてください。この約束の中の非同期エラーの自動伝播は非常に便利です。
ES6プロミス実装
これは、ブルーバード約束ライブラリなしでストレートES6の約束で行うことができますが、あなたは手でさらにいくつかのことを行う必要があるだろう:
List.findOne()
の操作をお約束する必要があります。
User.findOne()
の操作を約束する必要があります。
- あなたは普通の
doc.comments.map()
の反復を使用し、個々の約束をアレイに集めてから、Promise.map()
の代わりにその配列にPromise.all()
を使用しなければならないでしょう。
ここでは、コードです:
// manually promisify findOne
List.findOneAsync = function(queryObj) {
return new Promise(function(resolve, reject) {
List.findOne(queryObj, function(err, data) {
if (err) return reject(err);
resolve(data);
});
}
}
User.findOneAsync = function(queryObj) {
return new Promise(function(resolve, reject) {
User.findOne(queryObj, function(err, data) {
if (err) return reject(err);
resolve(data);
});
}
}
router.get('/:id', function (req, res, next) {
List.findOneAsync({listurl: req.params.id}).then(function(doc) {
if (!doc) {
throw "Empty Document";
}
var promises = doc.comments.map(function(item, index) {
return User.findOneAsync({Name: item.commenter}).then(function(data) {
if (data) {
item.gvUrl = gravatar.url(data.Email, {s: '200', r: 'pg', d: '404'});
} else {
item.gvUrl = 'noGravs';
}
});
});
return Promise.all(promises).then(function() {
return doc;
});
}).then(function(doc) {
res.render('list', {
appTitle: doc.Title,
list: doc
});
}).catch(function(err) {
res.status(404).render('404', {appTitle: "Book not found"});
});
}
にconsole.log(DOC)と、それは自分自身を未定義いないことを確認してください。しかしこれはvar z = 0より前である。 – Matt
dupが結果が「未定義」である理由を説明しているにもかかわらず、同時に複数の操作でこの特定の問題を解決する方法を説明していないため、投票を再開することができます。 – jfriend00