[英]How chain promise properly within Promise.all?
基本上,我從計划表中獲取所有行,然后處理它的單個行。 如果該行已在命令表中,請跳過。 否則,我將其插入。
我在Promise.all(rows.map(function(row){
return is_schedule_cmd_already_pending(schedule_id).then(function(num){
return insert_into_pending_cmd(num, schedule_id, device_name, cmd);
});
我在is_schedule_cmd_already_pending
和insert_into_pending_cmd
中的console.log中打印sql語句
打印順序不同步。
對於每一行,應該以這種方式以同步方式執行。
- schedule_id, device_name, day_code, schedule_time, schedule_hr, schedule_min, cmd -
- curr_date_code, curr_h, curr_min -
.. match up day ..
~~ match up hour ~~
## match up d-h-m, run ##
is_schedule_cmd_already_pending
insert_into_pending_cmd
- schedule_id, device_name, day_code, schedule_time, schedule_hr, schedule_min, cmd -
- curr_date_code, curr_h, curr_min -
.. match up day ..
~~ match up hour ~~
## match up d-h-m, run ##
is_schedule_cmd_already_pending
insert_into_pending_cmd
- schedule_id, device_name, day_code, schedule_time, schedule_hr, schedule_min, cmd -
- curr_date_code, curr_h, curr_min -
.. match up day ..
~~ match up hour ~~
## match up d-h-m, run ##
is_schedule_cmd_already_pending
insert_into_pending_cmd
.......
.......
相反,這就像(即所有insert_into_pending_cmd都發生在最后,這不是我想要的)
- schedule_id, device_name, day_code, schedule_time, schedule_hr, schedule_min, cmd -
- curr_date_code, curr_h, curr_min -
.. match up day ..
~~ match up hour ~~
## match up d-h-m, run ##
is_schedule_cmd_already_pending
- schedule_id, device_name, day_code, schedule_time, schedule_hr, schedule_min, cmd -
- curr_date_code, curr_h, curr_min -
.. match up day ..
~~ match up hour ~~
## match up d-h-m, run ##
is_schedule_cmd_already_pending
- schedule_id, device_name, day_code, schedule_time, schedule_hr, schedule_min, cmd -
- curr_date_code, curr_h, curr_min -
.. match up day ..
~~ match up hour ~~
## match up d-h-m, run ##
is_schedule_cmd_already_pending
.......
.......
.......
insert_into_pending_cmd
insert_into_pending_cmd
insert_into_pending_cmd
完整代碼
var config = require("./config.js");
var Promise = require('bluebird');
var mysql = require('promise-mysql');
var ON_DEATH = require('death');
var g_pool = null;
function connect_db() {
g_pool = mysql.createPool(config.db_config);
}
function close_db() {
g_pool.end(function (err) {
// all connections in the pool have ended
});
}
// http://thecodeship.com/web-development/alternative-to-javascript-evil-setinterval/
function interval(func, wait, times){
var interv = function(w, t) {
return function() {
if(typeof t === "undefined" || t-- > 0) {
setTimeout(interv, w);
try {
func.call(null);
}
catch(e) {
t = 0;
throw e.toString();
}
}
};
}(wait, times);
setTimeout(interv, wait);
}
function get_current_utc_time() {
var curr_date_obj = new Date();
var time_utc = "";
// somehow the date format is not accurate.
//var time_utc = dateFormat(now, "yyyy-mm-dd h:MM:ss", true);
var year = curr_date_obj.getUTCFullYear();
var month = add_zero(curr_date_obj.getUTCMonth() + 1); // count from 0
var date = add_zero(curr_date_obj.getUTCDate()); // count from 1
var hr = add_zero(curr_date_obj.getUTCHours());
var min = add_zero(curr_date_obj.getUTCMinutes());
// we ignore the second
var sec = "00";
time_utc = year + "-" + month + "-" + date + " " + hr + ":" + min + ":" + sec;
console.log("-current utc-");
console.log(time_utc);
return time_utc;
};
// http://www.w3schools.com/jsref/jsref_getutchours.asp
function add_zero(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
function insert_into_pending_cmd(msg_obj) {
console.log();
console.log("-insert_into_pending_cmd-");
var schedule_id = msg_obj.schedule_id;
var device_name = msg_obj.device_name;
var cmd = msg_obj.cmd;
var is_pending = msg_obj.is_pending;
if(is_pending) {
return Promise.resolve();
}
else {
var curr_time = get_current_utc_time();
var sql = "insert into Command set CommandDate = " + "'" + curr_time + "'" + "," + "RemoteName = " + "'" + device_name + "'" + "," + "CommandJSON = " + "'" + cmd + "'" + "," + "CommandComplete = 0" + "," + "ScheduleId = " + "'" + schedule_id + "'";
return g_pool.query(sql).then(function(){
return Promise.resolve();
});
}
}
function is_schedule_cmd_already_pending(msg_obj) {
console.log();
console.log("-is_schedule_cmd_already_pending-");
var schedule_id = msg_obj.schedule_id;
var device_name = msg_obj.device_name;
var cmd = msg_obj.cmd;
var is_run = msg_obj.is_run;
var local_msg_obj = {};
if(is_run) {
var sql = "select count(*) as num from Command where ScheduleId = " + "'" + schedule_id + "'" + " and CommandComplete = 0 and (UNIX_TIMESTAMP(UTC_TIMESTAMP()) - UNIX_TIMESTAMP(CommandDate)) < 600 and (UNIX_TIMESTAMP(UTC_TIMESTAMP()) - UNIX_TIMESTAMP(CommandDate)) > 0";
return g_pool.query(sql).then(function(rows){
var num = rows[0].num;
if(num == 0) {
local_msg_obj = {
schedule_id: schedule_id,
device_name: device_name,
cmd: cmd,
is_pending: false
};
return Promise.resolve(local_msg_obj);
}
else {
local_msg_obj = {
schedule_id: schedule_id,
device_name: device_name,
cmd: cmd,
is_pending: true
};
return Promise.resolve(local_msg_obj);
}
});
}
else {
local_msg_obj = {
schedule_id: schedule_id,
device_name: device_name,
cmd: cmd,
is_pending: true
};
return Promise.resolve(local_msg_obj);
}
}
function is_matchup_schedule_time(row) {
// get all field
var schedule_id = row.ScheduleId;
var device_name = row.ScheduleRemoteName;
var day_code = row.ScheduleDaycode;
var schedule_time = row.ScheduleTime;
var cmd = row.ScheduleCommandJSON;
// get hour and min
var schedule_time_arr = schedule_time.split(":");
var schedule_hour = schedule_time_arr[0];
var schedule_min = schedule_time_arr[1];
// print
console.log();
console.log();
console.log("- schedule_id, device_name, day_code, schedule_time, schedule_hr, schedule_min, cmd -");
console.log(schedule_id);
console.log(device_name);
console.log(day_code);
console.log(schedule_time);
console.log(schedule_hour);
console.log(schedule_min);
console.log(cmd);
// curr date obj
var curr_date_obj = new Date();
var curr_date_code = add_zero(curr_date_obj.getUTCDay());
// print current
console.log();
console.log("- curr_date_code, curr_h, curr_min - ");
console.log(curr_date_code);
console.log(add_zero(curr_date_obj.getUTCHours()));
console.log(add_zero(curr_date_obj.getUTCMinutes()));
// var
var msg_obj = {};
// Match up day
if(day_code == curr_date_code) {
console.log();
console.log(".. match up day ..");
// Match up hour
var curr_hour = add_zero(curr_date_obj.getUTCHours());
if(schedule_hour == curr_hour) {
console.log();
console.log("~~ match up hour ~~");
// Match up min
var curr_min = add_zero(curr_date_obj.getUTCMinutes());
if(schedule_min == curr_min) {
console.log();
console.log("## match up d-h-m, run ##");
msg_obj = {
schedule_id: schedule_id,
device_name: device_name,
cmd: cmd,
is_run: true
};
return Promise.resolve(msg_obj);
}
}
}
else {
}
//
msg_obj = {
schedule_id: schedule_id,
device_name: device_name,
cmd: cmd,
is_run: false
};
return Promise.resolve(msg_obj);
}
// NOTE -------------
function process_schedule_rows(rows) {
return Promise.mapSeries(rows, function(row) {
return is_matchup_schedule_time(row)
.then(is_schedule_cmd_already_pending)
.then(insert_into_pending_cmd)
.catch(function(e){
throw e;
})
});
}
function do_schedule() {
console.log();
console.log("---- start do_schedule ----");
g_pool.query("select * from Schedule order by ScheduleId asc")
.then(process_schedule_rows)
.catch(function(e){
throw e;
});
}
// main func
function main() {
console.log("db host:");
console.log(config.db_host);
connect_db();
interval(function(){
do_schedule();
}, 5000, undefined);
// Clean up
ON_DEATH(function(signal, err) {
console.log();
console.log("-- script interupted --");
console.log("close db");
// close db
close_db();
process.exit();
});
}
// run main func
main();
您的Promise.all()
模式並行運行所有行,其中處理這些行涉及的各種操作可以按任何順序完成。 這就是您的代碼設計為工作方式的方式。
要對它們進行排序,以便一次運行一行,而在上一行完全完成后再處理下一行,則需要使用其他設計模式。 序列化處理數組的promise的經典方法是使用.reduce()
如下所示:
// process each row sequentially
rows.reduce(function(p, row) {
return p.then(function() {
return is_schedule_cmd_already_pending(schedule_id).then(function(num) {
return insert_into_pending_cmd(num, schedule_id, device_name, cmd);
});
});
}, Promise.resolve()).then(function(data) {
// everything done here
}).catch(function(err) {
// error here
});
這將創建一個擴展的承諾鏈,其中每一行都作為承諾鏈中的一個步驟進行處理,並且鏈中的下一個鏈接要等到前一個鏈接完成后才會運行。
上面的方案與標准ES6承諾一起使用。 我個人更喜歡使用具有Promise.mapSeries()
的Bluebird Promise.mapSeries()
庫,該庫專門為此設計:
const Promise = require('bluebird');
Promise.mapSeries(rows, function(row) {
return is_schedule_cmd_already_pending(schedule_id).then(function(num) {
return insert_into_pending_cmd(num, schedule_id, device_name, cmd);
});
}).then(function(data) {
// everything done here
}).catch(function(err) {
// error here
});
僅供參考,實際代碼中的錯誤處理存在很多問題。 承諾使異步錯誤處理的數量級更容易。 如果您承諾較低級別的操作(或使用數據庫的Promise接口),然后僅在基於Promise的代碼中編寫控制流和邏輯,則編寫適當的錯誤處理將大為容易。 這樣的代碼行:
if (err) throw err;
在普通的異步回調中的代碼不會為您提供適當的錯誤處理。 對控制流中的所有內容使用promise,將非常容易傳播和處理異步錯誤。 實際上,使用嵌套的純異步回調正確執行此操作非常困難,並且您的代碼顯示了幾個錯誤。 將所有異步操作轉換為Promise,即可輕松正確地進行操作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.