2016-04-29 3 views
0

Этот маршрутизатор используется для доступа к определенному списку. В примере comments представлен массив объектов. Когда я вставляю этот gravatar.url(data.Email, {s: '200', r: 'pg', d: '404'}); в comments, объект успешно выполнен. Но на переднем конце (я использую нефрит). Я пытаюсь использовать этот list.comments[i].gvUrl, он возвращает undefined. Он даже возвращает неопределенный только конец цикла 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 (док) и убедитесь, что это не определено себя. но это до var z = 0; – Matt

+0

Голосование для повторного открытия, потому что, хотя дуб объясняет, почему результат «неопределен», он не объяснил, как наилучшим образом решить эту проблему со многими операциями в полете в одно и то же время. – jfriend00

ответ

1

Вы инициируют кучу асинхронных операций внутри for цикла и ожидает все асинхронные операции, которые будут выполняться, когда цикл for делается. Но на самом деле ни один из них еще не сделан, поэтому ваш массив doc.comments еще не заселен. Вы пытались использовать его до того, как оно было заполнено, поэтому вы видите, что оно определено там, где вы пытались его использовать.

Лучший способ решить эту задачу - научиться использовать обещания, а затем запускать несколько запросов с помощью чего-то типа Promise.map() или ES6 Promise.all(), а затем сообщить механизму обещания, когда все запросы будут выполнены.

Короткие преобразования базы данных звонков через использовать Promises, вы можете передать-код, чтобы знать, когда все делается следующим образом:

Ручной закодированы Ответный Реализация

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() s в петле.
  2. Эти асинхронные операции могут быть завершены в любом порядке.
  3. Эти операции асинхронного сканирования не будут завершены, когда цикл будет выполнен. Весь цикл, выполненный, инициирует операции. Они все закончится некоторое неопределенное время позже.
  4. Чтобы узнать, когда все операции асинхронизации выполнены, он поддерживает счетчик, чтобы подсчитать, сколько из них выполнено и отображает страницу, когда счетчик достигает количества запущенных в данный момент запросов. Это «ручной» способ узнать, когда все они сделаны.

Bluebird Promise Реализация

Вот как это может работать, используя Bluebird Обещания библиотеки и конвертирования операции базы данных для поддержки Обещания:

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. Добавьте многообещающие методы в объекты User и List, чтобы добавить новую версию каждого метода, который возвращает обещание.
  3. Звоните List.findOneAsync(). Суффикс "Async" имени метода представляет новый метод, добавленный .promisifyAll().
  4. Если нет doc, тогда бросок, который отклонит обещание, будет обработан в .catch() в конце. Обещания - это асинхронный сброс, который очень удобен для обработки ошибок async.
  5. Звоните Promise.map() на номер doc.comments. Это автоматически приведет к итерации массива doc.comments и вызовет итератор для каждого элемента массива (аналогично Array.prototype.map(), за исключением того, что здесь он собирает все обещания, возвращаемые итератором, и возвращает новое обещание, которое разрешается, когда устраняются все основные обещания. Кстати, это позволяет всем итераторам работать параллельно и говорит вам, когда все итераторы сделаны.
  6. итератор вызывает User.findOneAsync() и устанавливает значение doc.comments[index].gvUrl с результатом.
  7. Там есть дополнительный .then() обработчик на Promise.map() только к измените разрешенное значение этого обещания как объект doc, чтобы мы могли получить это от внешних обработчиков.
  8. Для успеха из внешнего обещания визуализируйте.
  9. Для получения информации об ошибке от внешнего обещания укажите страницу 404. Имейте в виду, что любые отклоненные обещания в любой точке всей этой схемы будут распространяться и быть отклонением на верхнем уровне. Это автоматическое распространение асинхронных ошибок в обещаниях чрезвычайно полезно.

ES6 Promise Реализация

Это может быть сделано с прямыми ES6 обещаниями без Bluebird библиотеки обещания, но вы должны сделать несколько вещей вручную:

  1. Вам нужно будет обезопасить операцию List.findOne().
  2. Вам нужно будет проделать операцию User.findOne().
  3. Вы должны выполнить обычную doc.comments.map() итерацию и собрать каждое отдельное обещание в массив, а затем использовать Promise.all() на этом массиве вместо того, чтобы позволить Promise.map() сделать все это за вас.

Вот код:

// 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

Вы, сэр, потрясающие! –