繁体   English   中英

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

[英]How do I write a synchronous while loop using node.js and promises

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

它当前的代码似乎对函数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;
}

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

看一下async.eachSeries()的文档( #each()别名)。 您将要提供第三个参数,并在async.whilst()的第二个函数中调用源自该参数的回调。

我认为以下修改将满足您的需求。 看一下我命名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;
}

旁注,如果你对它开放,使用像coasync/await这样的东西会在我看来很多地简化这段代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM