如何正确构建异步节点代码

时间:2015-01-09 22:10:49

标签: javascript node.js mongodb asynchronous mongoose

我正在尝试通过我拥有的“事件”数据库,并根据每个事件的位置,半径,开始时间和结束时间从Instagram API中提取照片。我在我的节点服务器上设置了以下代码,但它的行为并不像我期望的那样。

我运行此代码时看到的第一件事是为每个事件打印sending request to Instagram for [name] with min_timestamp: [timestamp]。我没想到这一点。我本来希望看到第一个事件记录这一行,然后一遍又一遍地更新新的时间戳,直到该事件到达结束时间。然后是事件2,遍历时间戳,依此类推。

我最终会为每个事件反复重复相同的照片。这就好像我的代码一次又一次地向Instagram发送一个请求(带有初始时间戳)然后停止。

关于我的timestamp变量的注释:对于每个事件,我将minTimestamp变量设置为最初等于我的数据库中的event.start。这是在发送给Instagram的请求中使用的。 Instagram向我返回最多20张照片。每张照片都有一个created_time变量。我抓住了最新的created_time变量,并将我的minTimestamp变量设置为等于它(minTimestamp = images[0].created_time;),以便我下次发送到Instagram的请求(以获取接下来的20张照片)。这一过程一直持续到minTimestamp不再小于endTimestamp(来自我的db的那个事件的event.end)。

server.js代码:

// modules =================================================
var express        = require('express.io');
var app            = express();
var port           = process.env.PORT || 6060;
var io             = require('socket.io').listen(app.listen(port));
var request        = require('request');
var Instagram      = require('instagram-node-lib');
var mongoose       = require('mongoose');
var async          = require('async');
var bodyParser     = require('body-parser');
var methodOverride = require('method-override');
var db             = require('./config/db');
var Event          = require('./app/models/event');

// configuration ===========================================
mongoose.connect(db.url); // connect to our mongoDB database

// get all data/stuff of the body (POST) parameters
app.use(bodyParser.json()); // parse application/json 
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse     application/vnd.api+json as json
app.use(bodyParser.urlencoded({ extended: true })); // parse application/x-www-form- urlencoded

app.use(methodOverride('X-HTTP-Method-Override')); // override with the X-HTTP-Method- Override header in the request. simulate DELETE/PUT
app.use(express.static(__dirname + '/public')); // set the static files location  /public/img will be /img for users

var baseUrl = 'https://api.instagram.com/v1/media/search?lat=';
var clientId = CLIENT-ID;

Event.find({}, function(err, events) {

    async.eachSeries(events, function(event, callback) {

      var name = event.event;
      var latitude = event.latitude;
      var longitude = event.longitude;
      var distance = event.radius;
      var minTimestamp = Math.floor(new Date(event.start).getTime()/1000);
      var endTimestamp = Math.floor(new Date(event.end).getTime()/1000);

      async.whilst(
        function () { return minTimestamp < Math.floor(Date.now() / 1000) && minTimestamp < endTimestamp; },
        function(callback) {
          console.log('sending request to Instagram for ' + name + ' with min_timestamp: ' + minTimestamp);
          request(baseUrl + latitude + '&lng=' + longitude + '&distance=' + distance + '&min_timestamp=' + minTimestamp + '&client_id=' + clientId,
            function (error, response, body) {
              if (error) { 
                console.log('error');
                return;
              }

                //JSON object with all the info about the image
                var imageJson = JSON.parse(body);
                var images = imageJson.data;
                var numImages = images.length;
                console.log(numImages + ' images returned with starting time ' + images[(numImages - 1)].created_time + ' and ending time ' + images[0].created_time);

                async.eachSeries(images, function(image, callback) {

                  //Save the new object to DB
                  Event.findOneAndUpdate( { $and: [{latitude: latitude}, {radius: distance}] }, { $push: {'photos':
                      { img: image.images.standard_resolution.url,
                        link: image.link,
                        username: image.user.username,
                        profile: image.user.profile_picture,
                        text: image.caption ? image.caption.text : '',
                        longitude: image.location.longitude,
                        latitude: image.location.latitude
                      }}},
                      { safe: true, upsert: false },
                      function(err, model) {
                          console.log(err);
                      }
                  );
                  console.log(numImages + ' images saved to db');
                  callback();
              }, function(err){
                  // if any of the file processing produced an error, err would equal that error
                  if( err ) {
                    // One of the iterations produced an error.
                    // All processing will now stop.
                    console.log('Images failed to process');
                  } else {
                    console.log('All images have been processed successfully');
                  }
              });

              minTimestamp = images[0].created_time;
              console.log('min_timestamp incremented to: ' + minTimestamp);
            }
          );
        },
        function (err) {

        }
      );
      callback();
    }, function(err){
        // if any of the file processing produced an error, err would equal that error
        if( err ) {
          // One of the iterations produced an error.
          // All processing will now stop.
          console.log('An event failed to process');
        } else {
          console.log('All events have been processed successfully');
        }
    });
});

// routes ==================================================
require('./app/routes')(app); // configure our routes

// start app ===============================================
console.log('Magic happens on port ' + port);           // shoutout to the user
exports = module.exports = app;                         // expose app

2 个答案:

答案 0 :(得分:1)

答案是你错过了对while位的回调()。

以下是一些代码来说明:

var async = require('async');

var minTimestamp = 1;
var endTimestamp = 10;

async.whilst(
    function () { minTimestamp < endTimestamp; },
    function(callback) {
      console.log('sending request to Instagram for name with min_timestamp: ' + minTimestamp);

      minTimestamp = minTimestamp + 1;
      console.log('min_timestamp incremented to: ' + minTimestamp);
      callback();
    },
    function (err) {
        if(err){
                throw err;
        }
    }
  );

如果我在没有callback()的情况下运行它,我会得到以下输出:

sending request to Instagram for name with min_timestamp: 1
min_timestamp incremented to: 2

如果我放回callback(),我会得到这个:

sending request to Instagram for name with min_timestamp: 1
min_timestamp incremented to: 2
sending request to Instagram for name with min_timestamp: 2
min_timestamp incremented to: 3
sending request to Instagram for name with min_timestamp: 3
min_timestamp incremented to: 4
sending request to Instagram for name with min_timestamp: 4
min_timestamp incremented to: 5
sending request to Instagram for name with min_timestamp: 5
min_timestamp incremented to: 6
sending request to Instagram for name with min_timestamp: 6
min_timestamp incremented to: 7
sending request to Instagram for name with min_timestamp: 7
min_timestamp incremented to: 8
sending request to Instagram for name with min_timestamp: 8
min_timestamp incremented to: 9
sending request to Instagram for name with min_timestamp: 9
min_timestamp incremented to: 10

因此在这里放一个callback():

      minTimestamp = images[0].created_time;
      console.log('min_timestamp incremented to: ' + minTimestamp);
      callback(); //missing callback
    }
 );
 },

答案 1 :(得分:0)

// modules =================================================
var express        = require('express.io');
var app            = express();
var port           = process.env.PORT || 6060;
var io             = require('socket.io').listen(app.listen(port));
var request        = require('request');
var Instagram      = require('instagram-node-lib');
var mongoose       = require('mongoose');
var async          = require('async');
var bodyParser     = require('body-parser');
var methodOverride = require('method-override');
var db             = require('./config/db');
var Event          = require('./app/models/event');

// configuration ===========================================
mongoose.connect(db.url); // connect to our mongoDB database

// get all data/stuff of the body (POST) parameters
app.use(bodyParser.json()); // parse application/json 
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse        application/vnd.api+json as json
app.use(bodyParser.urlencoded({ extended: true })); // parse application/x-www-form- urlencoded

app.use(methodOverride('X-HTTP-Method-Override')); // override with the X-HTTP-Method- Override  header in the request. simulate DELETE/PUT
app.use(express.static(__dirname + '/public')); // set the static files location  /public/img  will be /img for users

var baseUrl = 'https://api.instagram.com/v1/media/search?lat=';
var clientId = CLIENT-ID;

Event.find({}, function(err, events) {

  async.eachSeries(events, function(event, seriesCallback) {

    var name = event.event;
    var latitude = event.latitude;
    var longitude = event.longitude;
    var distance = event.radius;
    var minTimestamp = Math.floor(new Date(event.start).getTime()/1000);
    var endTimestamp = Math.floor(new Date(event.end).getTime()/1000);

    async.whilst(
      function () { return minTimestamp < Math.floor(Date.now() / 1000) && minTimestamp <  endTimestamp; },
        function(requestFinishedCallback) {
          console.log('sending request to Instagram for ' + name + ' with min_timestamp: ' + minTimestamp);
          request(baseUrl + latitude + '&lng=' + longitude + '&distance=' + distance + '&min_timestamp=' + minTimestamp + '&client_id=' + clientId,
            function (error, response, body) {
              if (error) { 
                console.log('error');
                return;
              }

              //JSON object with all the info about the image
              var imageJson = JSON.parse(body);
              var images = imageJson.data;
              var numImages = images.length;
              console.log(numImages + ' images returned with starting time ' + images[(numImages - 1)].created_time + ' and ending time ' + images[0].created_time);

              async.eachSeries(images, function(image, imageFinishedCallback) {

                //Save the new object to DB
                Event.findOneAndUpdate( { $and: [{latitude: latitude}, {radius: distance}] }, { $push: {'photos':
                  { img: image.images.standard_resolution.url,
                    link: image.link,
                    username: image.user.username,
                    profile: image.user.profile_picture,
                    text: image.caption ? image.caption.text : '',
                    longitude: image.location.longitude,
                    latitude: image.location.latitude
                  }}},
                  { safe: true, upsert: false },
                  function(err, model) {
                    console.log(err);
                    console.log('Image processed');  
                    imageFinishedCallback();
                  }
                );

              }, function(err){
                   // if any of the image processing produced an error, err would equal that error
                   if( err ) {
                     // One of the iterations produced an error.
                     // All processing will now stop.
                     console.log('Images failed to process');
                   } else {
                     minTimestamp = images[0].created_time;
                     console.log(numImages + ' images have been processed successfully and min_timestamp has been incremented to: ' + minTimestamp);
                     requestFinishedCallback();
               }
                 });
                }
              );
           }, function(err){
                   // if any of the image processing produced an error, err would equal that error
                   if( err ) {
                     // One of the iterations produced an error.
                     // All processing will now stop.
                     console.log('Event failed to process');
                   } else {
                        console.log(name + ' has been fully processed successfully with final min_timestamp of: ' + minTimestamp);
                   }
                   seriesCallback();
                });
             }, function(err){
                    // if any of the image processing produced an error, err would equal that error
                    if( err ) {
                      // One of the iterations produced an error.
                      // All processing will now stop.
                      console.log('Something failed to process');
                    } else {
                         console.log('All events have been processed successfully');
                    }
                 });
}); 

// routes ==================================================
require('./app/routes')(app); // configure our routes

// start app ===============================================
console.log('Magic happens on port ' + port);           // shoutout to the user
exports = module.exports = app;