2016-04-29 11 views
0

このルータは特定のリストにアクセスするために使用されます。この例では、commentsはオブジェクトの配列です。このgravatar.url(data.Email, {s: '200', r: 'pg', d: '404'});commentsに入れてしまえば正常に終了しました。しかし、フロントエンド(私はヒスイを使用します)。私はこれを使用しようとするとlist.comments[i].gvUrl、それは未定義を返します。それはforループの外側に未定義を返します。何が間違っているのですか?NodeJSオブジェクトは未定義を返します

router.get('/:id', function (req, res, next) { 
    List.findOne({listurl: req.params.id}, function (err, doc) { 
     var z = 0; 
     if (!err && doc != null) { 
      for (var i = 0; i < doc.comments.length; i++) { 
       User.findOne({Name: doc.comments[i].commenter}, function (err, data) { 
        if (data) { 
         doc.comments[z++].gvUrl = gravatar.url(data.Email, {s: '200', r: 'pg', d: '404'}); 
        } else { 
         doc.comments[z++].gvUrl = 'noGravs'; 
        } 
       }); 
      } 

      //this line returns unfefined; 
       console.log(doc.comments[0].gvUrl); 

       res.render('list', { 
        appTitle: doc.Title, 
        list: doc 
       }); 
     } 
     else { 
      res.status(404).render('404', {appTitle: "Book not found"}); 
     } 
    }); 
}); 
+0

にconsole.log(DOC)と、それは自分自身を未定義いないことを確認してください。しかしこれはvar z = 0より前である。 – Matt

+0

dupが結果が「未定義」である理由を説明しているにもかかわらず、同時に複数の操作でこの特定の問題を解決する方法を説明していないため、投票を再開することができます。 – jfriend00

答えて

1

あなたは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"}); 
     } 
    }); 
} 

このコードは認識し

  1. User.findOne()非同期操作を実行していますループの中で。
  2. これらの非同期操作は、任意の順序で終了できます。
  3. これらの非同期操作は、ループが完了しても完了しません。すべてのループが完了すると、操作が開始されます。彼らはすべて後で不確定な時間を完了します。
  4. すべての非同期操作がいつ完了したかを知るために、開始された合計要求数にカウントが達したときに、完了したページ数をカウントするカウンタを保持します。これは、すべてが完了した時を知る方法に対する「手作業」方法です。

ブルーバードの約束の実現

は、ここではブルーバードを使用して仕事ができる方法ですライブラリを約束し、約束をサポートするようにデータベース操作を変換:

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"}); 
    }); 
} 

はここで、これはどのように動作するかです:

  1. Bluebird約束ライブラリをロードする
  2. 約束したメソッドをUserListオブジェクトに追加して、約束を返す新しいメソッドを追加します。
  3. List.findOneAsync()と呼んでください。メソッド名の"Async"接尾辞は、.promisifyAll()が追加した新しいメソッドを表します。
  4. docがない場合、約束を拒否するスローは、最後に.catch()で処理されます。プロミスは、非同期エラー処理のために非常に便利な非同期スローセーフです。
  5. doc.commentsPromise.map()を呼び出します。これにより、doc.comments配列が自動的に反復され、配列内の各項目のイテレータが呼び出されます(Array.prototype.map()に似ていますが、イテレータから返されたすべての約束を集め、すべての根本的な約定が解決されたときに解決されます)。道、それはすべてのイテレータを並列に実行することができますし、すべてのイテレータが行われたときにわかります。
  6. イテレータはUser.findOneAsync()を呼び出し、結果とdoc.comments[index].gvUrl値を設定します。
  7. Promise.map()に余分.then()ハンドラはちょうどにありますその約束の解決された値をdocオブジェクトに変更して、それを外側の約束ハンドラから得ることができます。
  8. 外側の約束から成功するために、レンダリングします。
  9. 外側の約束からのエラーについては、404ページを表示してください。このスキームのどこかで拒否された約束は、伝播してトップレベルで拒否されることを覚えておいてください。この約束の中の非同期エラーの自動伝播は非常に便利です。

ES6プロミス実装

これは、ブルーバード約束ライブラリなしでストレートES6の約束で行うことができますが、あなたは手でさらにいくつかのことを行う必要があるだろう:

  1. List.findOne()の操作をお約束する必要があります。
  2. User.findOne()の操作を約束する必要があります。
  3. あなたは普通の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"}); 
    }); 
} 
+0

あなたは最高です! –

関連する問題