如何使用node.js和promises编写同步while循环

时间:2016-04-19 23:19:02

标签: javascript node.js promise

我想调用一个函数,它为每个学生提供特定时间间隔的数据,并重复相同的过程,直到达到某种条件。

当前代码似乎对函数iterateThruAllStudents()进行并行调用;

我的代码如下所示:

var startDate = new Date("March 16, 2016 00:00:00");  //Start from February

var fromTimestamp = null;
var toTimestamp = null;
var today = new Date();
var todayTimestamp = new Date(today).getTime() / 1000;

async.whilst(
    function () {
        return fromTimestamp < todayTimestamp;
    },
    function (callback) {
        console.log(startDate);
        fromTimestamp = new Date(startDate).getTime() / 1000;
        startDate.setDate(startDate.getDate() + 5);
        toTimestamp = new Date(startDate).getTime() / 1000;
        iterateThruAllStudents(fromTimestamp, toTimestamp);
        callback(null, startDate);
    },
    function (err, n) {
        console.log('finished for ' + n);
    }
);

function iterateThruAllStudents(from, to) {
    Student.find({status: 'student'})
        .populate('user')
        .exec(function (err, students) {
            if (err) {
                throw err;
            }

            var counter = 0;
            async.eachSeries(students, function iteratee(student, callback) {
                if (student.worksnap.user != null) {
                    var worksnapOptions = {
                        hostname: 'api.worksnaps.com',
                        path: '/api/projects/' + project_id + '/time_entries.xml?user_ids=' + student.worksnap.user.user_id + '&from_timestamp=' + from + '&to_timestamp=' + to,
                        headers: {
                            'Authorization': 'Basic ' + auth_hash
                        },
                        method: 'GET'
                    };

                    getTimeEntries(worksnapOptions)
                        .then(function (response) { //callback invoked on deferred.resolve
                            return convertXMLToJson(response);
                        }).then(function (timeEntries) {
                            console.log('convert xml to json');
                            var isEmpty = _.isEmpty(timeEntries); // true
                            if (isEmpty) {
                                callback(null);
                            }
                            return saveTimeEntry(timeEntries);
                        }).then(function (response) {
                            counter++;
                            console.log('all timeEntries for one student finished....Student: ' + student.worksnap.user.user_id + ' Student Counter: ' + counter);
                            callback(null);
                        });
                } else {
                    callback(null);
                }
            });
        });
}

function convertXMLToJson(response) {
    var deferred = Q.defer();
    parser.parseString(response, function (err, results) {
        if (err) {
            deferred.reject(err);
        }
        var json_string = JSON.stringify(results.time_entries);
        var timeEntries = JSON.parse(json_string);
        deferred.resolve(timeEntries);
    });
    return deferred.promise;
}

function saveTimeEntry(timeEntries) {
    var deferred = Q.defer();
    _.forEach(timeEntries.time_entry, function (item) {
        Student.findOne({
                'worksnap.user.user_id': item.user_id[0]
            })
            .populate('user')
            .exec(function (err, student) {
                if (err) {
                    deferred.reject(err);
                }
                student.worksnap.timeEntries.push(item);
                student.save(function (err) {
                    if (err) {
                        deferred.reject(err);
                    } else {
                        //console.log(item);
                    }
                });

            });
    });
    deferred.resolve('finished saving timeEntries for one student...');

    return deferred.promise;
}

function getTimeEntries(requestOptions) {
    //create a deferred object from Q
    process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
    var deferred = Q.defer();
    var req = http.request(requestOptions, function (response) {
        //set the response encoding to parse json string
        response.setEncoding('utf8');
        var responseData = '';
        //append data to responseData variable on the 'data' event emission
        response.on('data', function (data) {
            responseData += data;
        });
        //listen to the 'end' event
        response.on('end', function () {
            //resolve the deferred object with the response
            console.log('http call finished');
            deferred.resolve(responseData);
        });
    });

    //listen to the 'error' event
    req.on('error', function (err) {
        //if an error occurs reject the deferred
        console.log('inside On error.');
        console.log(err);
        deferred.reject(err);
    });
    req.end();
    //we are returning a promise object
    //if we returned the deferred object
    //deferred object reject and resolve could potentially be modified
    //violating the expected behavior of this function
    return deferred.promise;
}

任何人都知道如何实现这样的事情,我可以为每个时间间隔同步获取所有学生的数据?

1 个答案:

答案 0 :(得分:0)

查看async.eachSeries()的文档(done的别名)。你需要提供第三个参数,并调用源自var startDate = new Date("March 16, 2016 00:00:00"); //Start from February var fromTimestamp = null; var toTimestamp = null; var today = new Date(); var todayTimestamp = new Date(today).getTime() / 1000; async.whilst( function () { return fromTimestamp < todayTimestamp; }, function (callback) { console.log(startDate); fromTimestamp = new Date(startDate).getTime() / 1000; startDate.setDate(startDate.getDate() + 5); toTimestamp = new Date(startDate).getTime() / 1000; iterateThruAllStudents(fromTimestamp, toTimestamp, callback); }, function (err, n) { console.log('finished for ' + n); } ); function iterateThruAllStudents(from, to, done) { Student.find({status: 'student'}) .populate('user') .exec(function (err, students) { if (err) { throw err; } var counter = 0; async.eachSeries(students, function iteratee(student, callback) { if (student.worksnap.user != null) { var worksnapOptions = { hostname: 'api.worksnaps.com', path: '/api/projects/' + project_id + '/time_entries.xml?user_ids=' + student.worksnap.user.user_id + '&from_timestamp=' + from + '&to_timestamp=' + to, headers: { 'Authorization': 'Basic ' + auth_hash }, method: 'GET' }; getTimeEntries(worksnapOptions) .then(function (response) { //callback invoked on deferred.resolve return convertXMLToJson(response); }).then(function (timeEntries) { console.log('convert xml to json'); var isEmpty = _.isEmpty(timeEntries); // true if (isEmpty) { callback(null); } return saveTimeEntry(timeEntries); }).then(function (response) { counter++; console.log('all timeEntries for one student finished....Student: ' + student.worksnap.user.user_id + ' Student Counter: ' + counter); callback(null); }); } else { callback(null); } }, function eachSeriesFinished(err) { if (err) return done(err); return done(null, to); }); }); } function convertXMLToJson(response) { var deferred = Q.defer(); parser.parseString(response, function (err, results) { if (err) { deferred.reject(err); } var json_string = JSON.stringify(results.time_entries); var timeEntries = JSON.parse(json_string); deferred.resolve(timeEntries); }); return deferred.promise; } function saveTimeEntry(timeEntries) { var deferred = Q.defer(); _.forEach(timeEntries.time_entry, function (item) { Student.findOne({ 'worksnap.user.user_id': item.user_id[0] }) .populate('user') .exec(function (err, student) { if (err) { deferred.reject(err); } student.worksnap.timeEntries.push(item); student.save(function (err) { if (err) { deferred.reject(err); } else { //console.log(item); } }); }); }); deferred.resolve('finished saving timeEntries for one student...'); return deferred.promise; } function getTimeEntries(requestOptions) { //create a deferred object from Q process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; var deferred = Q.defer(); var req = http.request(requestOptions, function (response) { //set the response encoding to parse json string response.setEncoding('utf8'); var responseData = ''; //append data to responseData variable on the 'data' event emission response.on('data', function (data) { responseData += data; }); //listen to the 'end' event response.on('end', function () { //resolve the deferred object with the response console.log('http call finished'); deferred.resolve(responseData); }); }); //listen to the 'error' event req.on('error', function (err) { //if an error occurs reject the deferred console.log('inside On error.'); console.log(err); deferred.reject(err); }); req.end(); //we are returning a promise object //if we returned the deferred object //deferred object reject and resolve could potentially be modified //violating the expected behavior of this function return deferred.promise; } 的第二个函数中的参数的回调。

我认为以下修改可以满足您的需求。看一下我命名为co的回调,特别是:

async/await

旁注,如果您对此开放,使用{{1}}或{{1}}之类的内容会在我看来简化此代码 lot