返回承诺不适用于 AWS SDK
Returning promises won't work for AWS SDK
我创建了一个 API,它调用 get cloudWatch AWS API 并返回可以在我的应用程序上绘制的数据点。我为每个包裹都有单独的路线(如下面的路线代码所示)。此 API 使用 REST MVC 方法。
所以我正在用我的函数做一些事情。
从SQLite3数据库中读取EC2实例数据进行抓取
有关 运行 实例的信息(IP,instance_id,
instance_launchtime) 这样我就可以把它放在需要的参数中
对于来自 AWS SDK 的 getMetricStatistics API。
然后将第 1 步中的数据放入参数数组(3 个参数以 3 个不同的度量数据点响应)。这遍历每个参数,将其插入 getMetricStatistics API(一个一个地插入,因为 getMetricStatistics 不一次接受多个指标)以获取该实例的数据点并将它们推送到数组。
因为我相信数据库是异步的,这就是我对它附加承诺的原因。 当我将端点加载到我的浏览器中时,它一直在加载,不会显示任何数据。但是,当我刷新页面时,它会正确显示所有结果...
这是我的 API 控制器:
// Return results sent from Cloud Watch API
const InsightModel = require('../models/insight.model.js');
const cloudWatch = InsightModel.cloudWatch;
const CWParams = InsightModel.CWParams;
const packageById = InsightModel.packageById;
let cpuUtilParam;
let cpuCBParam;
let cpuCUParam;
let insightParams = [];
let metricResults = [];
exports.getAnalytics = (req, res) => {
const currentDate = new Date().toISOString();
let promise1 = new Promise((resolve, reject) => {
packageById(req.params.packageKey, (err, data) => {
if (err) {
reject(
res.status(500).send({
message:
err.message ||
'Error while getting the insight configuration data.',
})
);
} else {
cpuUtilParam = new CWParams(
currentDate,
'CPUUtilization',
'AWS/EC2',
data[0].launch_time,
data[0].instance_id
);
cpuCBParam = new CWParams(
currentDate,
'CPUCreditBalance',
'AWS/EC2',
data[0].launch_time,
data[0].instance_id
);
cpuCUParam = new CWParams(
currentDate,
'CPUCreditUsage',
'AWS/EC2',
data[0].launch_time,
data[0].instance_id
);
insightParams = [cpuUtilParam, cpuCBParam, cpuCUParam];
resolve(insightParams);
}
});
})
let promise2 = new Promise((resolve, reject) => {
insightParams.forEach(metric => {
cloudWatch.getMetricStatistics(metric, function(err, data) {
if (err) {
reject(
res.status(500).send({
messaage:
err.message ||
'Error occured while running cloudWatch getMetricStatistcs API: ',
})
);
} else {
metricResults.push(data);
if (metricResults.length === insightParams.length)
resolve(metricResults);
}
});
});
});
Promise.all([promise1, promise2])
.then(metricResults => {
res.send(metricResults);
console.log('AWS CW API successful');
})
.catch(err => {
res.status(500).send({
messaage:
err.message ||
'Error occured while reading in a promise from cloudWatch getMetricStatistcs API: ',
})
});
metricResults = [];
};
API的型号:
// Call AWS Cost Explorer API
const AWS = require('aws-sdk');
const config = require('./AWSconfig');
const database = require('./db');
const insightdb = database.insightdb;
AWS.config.update({
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
region: config.region,
});
//Linking AWS CloudWatch Service
var cloudWatch = new AWS.CloudWatch();
const packageById = (packageId, callback) => {
insightdb.all(
'SELECT * FROM ec2Instance WHERE package_id == ?',
packageId,
(err, rows) => {
if (err) {
callback(err, null);
} else {
callback(null, rows);
}
}
);
};
// Parameter class to feed into the CloudWatch getMetricStatistics function
const CWParams = function(reqDate, metricName,service,launchTime,instanceId) {
(this.EndTime = reqDate) /* required */,
(this.MetricName = metricName) /* required */,
(this.Namespace = service) /* required */,
(this.Period = 3600) /* required */,
(this.StartTime = launchTime) /* ${createDate}`, required */,
(this.Dimensions = [
{
Name: 'InstanceId' /* required */,
Value: instanceId /* required */,
},
]),
(this.Statistics = ['Maximum']);
};
//Exports variables to the controller (so they can be re-used)
module.exports = { cloudWatch, CWParams, packageById };
API的路线:
module.exports = app => {
const insight = require('../controllers/insight.controller.js');
app.get('/insights/aws/:packageKey', insight.getAnalytics);
};
就目前而言,在第二个 Promise 构造函数中,insightParams 保证尚未组合,因为 insightParams = [.....]
处于异步调用的回调中。因此,程序流程需要确保所有 "promise2" 的东西只在 "promise1" 完成后发生。
如果异步函数在尽可能低的级别 "promisified",那么在更高级别的代码中事情会变得简单得多。所以在模型中做两件事:
- 承诺
cloudWatch.getMetricStatistics()
- 将 packageById() 写入 return
Promise
而不是接受回调。
模型因此变成:
const AWS = require('aws-sdk'); // no change
const config = require('./AWSconfig'); // no change
const database = require('./db'); // no change
const insightdb = database.insightdb; // no change
AWS.config.update({
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
region: config.region
}); // no change
var cloudWatch = new AWS.CloudWatch(); // no change
// Promisify cloudWatch.getMetricStatistics() as cloudWatch.getMetricStatisticsAsync().
cloudWatch.getMetricStatisticsAsync = (metric) => {
return new Promise((resolve, reject) => {
cloudWatch.getMetricStatistics(metric, function(err, data) {
if (err) {
if(!err.message) { // Probably not necessary but here goes ...
err.message = 'Error occured while running cloudWatch getMetricStatistcs API: ';
}
reject(err); // (very necessary)
} else {
resolve(data);
}
});
});
};
// Ensure that packageById() returns Promise rather than accepting a callback.
const packageById = (packageId) => {
return new Promise((resolve, reject) => {
insightdb.all('SELECT * FROM ec2Instance WHERE package_id == ?', packageId, (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
};
现在getAnalytics()
可以这样写:
exports.getAnalytics = (req, res) => {
packageById(req.params.packageKey)
.then(data => {
const currentDate = new Date().toISOString();
let insightParams = [
new CWParams(currentDate, 'CPUUtilization', 'AWS/EC2', data[0].launch_time, data[0].instance_id),
new CWParams(currentDate, 'CPUCreditBalance', 'AWS/EC2', data[0].launch_time, data[0].instance_id),
new CWParams(currentDate, 'CPUCreditUsage', 'AWS/EC2', data[0].launch_time, data[0].instance_id)
];
// Composition of `insightParams` is synchronous so you can continue
// with the `cloudWatch.getMetricStatisticsAsync()` stuff inside the same .then().
return Promise.all(insightParams.map(metric => cloudWatch.getMetricStatisticsAsync(metric))); // Simple because of the Promisification above.
}, err => {
// This callback handles error from packageById() only,
// and is probably unnecessary but here goes ...
if(!err.message) {
err.message = 'Error while getting the insight configuration data.';
}
throw err;
})
.then(metricResults => {
res.send(metricResults);
console.log('AWS CW API successful');
})
.catch(err => {
// Any async error arising above will drop through to here.
res.status(500).send({
'message': err.message
}));
});
};
请注意,多个 res.status(500).send()
的捕获不是必需的。 Promise 链下的错误传播允许单个终端 .catch()
我创建了一个 API,它调用 get cloudWatch AWS API 并返回可以在我的应用程序上绘制的数据点。我为每个包裹都有单独的路线(如下面的路线代码所示)。此 API 使用 REST MVC 方法。
所以我正在用我的函数做一些事情。
从SQLite3数据库中读取EC2实例数据进行抓取 有关 运行 实例的信息(IP,instance_id, instance_launchtime) 这样我就可以把它放在需要的参数中 对于来自 AWS SDK 的 getMetricStatistics API。
然后将第 1 步中的数据放入参数数组(3 个参数以 3 个不同的度量数据点响应)。这遍历每个参数,将其插入 getMetricStatistics API(一个一个地插入,因为 getMetricStatistics 不一次接受多个指标)以获取该实例的数据点并将它们推送到数组。
因为我相信数据库是异步的,这就是我对它附加承诺的原因。 当我将端点加载到我的浏览器中时,它一直在加载,不会显示任何数据。但是,当我刷新页面时,它会正确显示所有结果...
这是我的 API 控制器:
// Return results sent from Cloud Watch API
const InsightModel = require('../models/insight.model.js');
const cloudWatch = InsightModel.cloudWatch;
const CWParams = InsightModel.CWParams;
const packageById = InsightModel.packageById;
let cpuUtilParam;
let cpuCBParam;
let cpuCUParam;
let insightParams = [];
let metricResults = [];
exports.getAnalytics = (req, res) => {
const currentDate = new Date().toISOString();
let promise1 = new Promise((resolve, reject) => {
packageById(req.params.packageKey, (err, data) => {
if (err) {
reject(
res.status(500).send({
message:
err.message ||
'Error while getting the insight configuration data.',
})
);
} else {
cpuUtilParam = new CWParams(
currentDate,
'CPUUtilization',
'AWS/EC2',
data[0].launch_time,
data[0].instance_id
);
cpuCBParam = new CWParams(
currentDate,
'CPUCreditBalance',
'AWS/EC2',
data[0].launch_time,
data[0].instance_id
);
cpuCUParam = new CWParams(
currentDate,
'CPUCreditUsage',
'AWS/EC2',
data[0].launch_time,
data[0].instance_id
);
insightParams = [cpuUtilParam, cpuCBParam, cpuCUParam];
resolve(insightParams);
}
});
})
let promise2 = new Promise((resolve, reject) => {
insightParams.forEach(metric => {
cloudWatch.getMetricStatistics(metric, function(err, data) {
if (err) {
reject(
res.status(500).send({
messaage:
err.message ||
'Error occured while running cloudWatch getMetricStatistcs API: ',
})
);
} else {
metricResults.push(data);
if (metricResults.length === insightParams.length)
resolve(metricResults);
}
});
});
});
Promise.all([promise1, promise2])
.then(metricResults => {
res.send(metricResults);
console.log('AWS CW API successful');
})
.catch(err => {
res.status(500).send({
messaage:
err.message ||
'Error occured while reading in a promise from cloudWatch getMetricStatistcs API: ',
})
});
metricResults = [];
};
API的型号:
// Call AWS Cost Explorer API
const AWS = require('aws-sdk');
const config = require('./AWSconfig');
const database = require('./db');
const insightdb = database.insightdb;
AWS.config.update({
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
region: config.region,
});
//Linking AWS CloudWatch Service
var cloudWatch = new AWS.CloudWatch();
const packageById = (packageId, callback) => {
insightdb.all(
'SELECT * FROM ec2Instance WHERE package_id == ?',
packageId,
(err, rows) => {
if (err) {
callback(err, null);
} else {
callback(null, rows);
}
}
);
};
// Parameter class to feed into the CloudWatch getMetricStatistics function
const CWParams = function(reqDate, metricName,service,launchTime,instanceId) {
(this.EndTime = reqDate) /* required */,
(this.MetricName = metricName) /* required */,
(this.Namespace = service) /* required */,
(this.Period = 3600) /* required */,
(this.StartTime = launchTime) /* ${createDate}`, required */,
(this.Dimensions = [
{
Name: 'InstanceId' /* required */,
Value: instanceId /* required */,
},
]),
(this.Statistics = ['Maximum']);
};
//Exports variables to the controller (so they can be re-used)
module.exports = { cloudWatch, CWParams, packageById };
API的路线:
module.exports = app => {
const insight = require('../controllers/insight.controller.js');
app.get('/insights/aws/:packageKey', insight.getAnalytics);
};
就目前而言,在第二个 Promise 构造函数中,insightParams 保证尚未组合,因为 insightParams = [.....]
处于异步调用的回调中。因此,程序流程需要确保所有 "promise2" 的东西只在 "promise1" 完成后发生。
如果异步函数在尽可能低的级别 "promisified",那么在更高级别的代码中事情会变得简单得多。所以在模型中做两件事:
- 承诺
cloudWatch.getMetricStatistics()
- 将 packageById() 写入 return
Promise
而不是接受回调。
模型因此变成:
const AWS = require('aws-sdk'); // no change
const config = require('./AWSconfig'); // no change
const database = require('./db'); // no change
const insightdb = database.insightdb; // no change
AWS.config.update({
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
region: config.region
}); // no change
var cloudWatch = new AWS.CloudWatch(); // no change
// Promisify cloudWatch.getMetricStatistics() as cloudWatch.getMetricStatisticsAsync().
cloudWatch.getMetricStatisticsAsync = (metric) => {
return new Promise((resolve, reject) => {
cloudWatch.getMetricStatistics(metric, function(err, data) {
if (err) {
if(!err.message) { // Probably not necessary but here goes ...
err.message = 'Error occured while running cloudWatch getMetricStatistcs API: ';
}
reject(err); // (very necessary)
} else {
resolve(data);
}
});
});
};
// Ensure that packageById() returns Promise rather than accepting a callback.
const packageById = (packageId) => {
return new Promise((resolve, reject) => {
insightdb.all('SELECT * FROM ec2Instance WHERE package_id == ?', packageId, (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
};
现在getAnalytics()
可以这样写:
exports.getAnalytics = (req, res) => {
packageById(req.params.packageKey)
.then(data => {
const currentDate = new Date().toISOString();
let insightParams = [
new CWParams(currentDate, 'CPUUtilization', 'AWS/EC2', data[0].launch_time, data[0].instance_id),
new CWParams(currentDate, 'CPUCreditBalance', 'AWS/EC2', data[0].launch_time, data[0].instance_id),
new CWParams(currentDate, 'CPUCreditUsage', 'AWS/EC2', data[0].launch_time, data[0].instance_id)
];
// Composition of `insightParams` is synchronous so you can continue
// with the `cloudWatch.getMetricStatisticsAsync()` stuff inside the same .then().
return Promise.all(insightParams.map(metric => cloudWatch.getMetricStatisticsAsync(metric))); // Simple because of the Promisification above.
}, err => {
// This callback handles error from packageById() only,
// and is probably unnecessary but here goes ...
if(!err.message) {
err.message = 'Error while getting the insight configuration data.';
}
throw err;
})
.then(metricResults => {
res.send(metricResults);
console.log('AWS CW API successful');
})
.catch(err => {
// Any async error arising above will drop through to here.
res.status(500).send({
'message': err.message
}));
});
};
请注意,多个 res.status(500).send()
的捕获不是必需的。 Promise 链下的错误传播允许单个终端 .catch()