我有一个代码片段(如下),它将基于几个参数生成请求.通过区分每个用户的请求,它实质上会创建类似于JBehaves的负载.在大多数情况下,这可以正常工作.请求的生成按预期工作.但是,结果并不像使用Promise.all()所期望的那样起作用.这引出我的问题:
Promise.all()有问题吗?
结果的格式在这个问题上可能看起来有些奇怪,但是,基本上,我只是在创建一个用户数组(它本身只是一个请求结果数组).
实际结果
而不是数组中的每个对象都不同,它们都是相同的.在大多数情况下,它似乎是推送到数组中的最后一个对象(但我尚未完全确认这一点).最初,这种行为使我相信我的代码段中存在范围界定问题,但我一直无法找到它:(
[
[{
hostname: 'google.com',
headers: [Object],
path: '/url1/',
method: 'GET',
date: 1457395032277,
status: 200,
ttfb: 1488
}, {
hostname: 'google.com',
headers: [Object],
path: '/url1/',
method: 'GET',
date: 1457395032277,
status: 200,
ttfb: 1488
}]
]
预期成绩
我希望Promise.all()返回一个(promise)解析为具有多个对象的数组-每个对象都不同,以反映task()中定义的每个任务的结果.
[
[{
hostname: 'google.com',
headers: [Object],
path: '/url1/',
method: 'GET',
date: 1457395032277,
status: 200,
ttfb: 1488
}, {
hostname: 'bing.com',
headers: [Object],
path: '/url2/',
method: 'GET',
date: 1457395032280,
status: 500,
ttfb: 501
}]
]
代码段
如果您注意到注释掉的console.dir(stats):该行按预期吐出结果(每个任务的结果不同),但是,如果我在reduce的结尾打了一个.then(),则该数组返回为Actual结果(与预期结果)
(为简便起见,我们假设request()返回一个Promise)
'use strict';
const request = require('./request');
const Promise = require('bluebird');
module.exports = (tests, options) => {
return Promise.all(users());
////////////
/* generate users */
function users() {
let users = [];
for (let x = options.users; x > 0; x--) {
/* https://github.com/petkaantonov/bluebird/issues/70#issuecomment-32256273 */
let user = Promise.reduce(tasks(), (values, task) => {
return task().then((stats) => {
// console.dir(stats);
values.push(stats);
return values;
});
}, []);
users.push(user);
};
return users;
}
/* generate requests per user */
function tasks() {
let tasks = [];
for (let id of Object.keys(tests)) {
for (let x = options.requests; x > 0; x--) {
let task = () => {
let delay = options.delay * 1000;
return Promise.delay(delay).then(() => request(tests[id]));
};
tasks.push(task);
};
}
return tasks;
}
};
索取片段
'use strict';
const https = require('follow-redirects').https;
const sprintf = require('util').format;
const Promise = require('bluebird');
process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;
let request = (req) => {
return new Promise((resolve) => {
let start = Date.now();
let ttfb;
let cb = (res) => {
req.status = res.statusCode;
res.on('data', (chunk) => {
if (!ttfb) ttfb = Date.now() - start;
});
res.on('end', () => {
req.ttfb = ttfb;
req.end = Date.now() - start;
resolve(req);
});
res.on('error', (err) => {
req.error = err;
resolve(req);
});
};
/* convert cookies for convenience */
if (req.headers.cookies) {
let cookies = [];
for (let cookie of Object.keys(req.headers.cookies)) {
cookies.push(sprintf('%s=%s', cookie, req.headers.cookies[cookie]));
}
req.headers.cookie = cookies.join('; ');
req.cookies = req.headers.cookies;
delete req.headers.cookies;
}
https.request(req, cb).end();
});
};
module.exports = request;
使用
$npm --version
2.14.12
$node --version
v0.12.9
任何帮助将不胜感激!
解决方法:
Is there an issue with
Promise.all()
?
没有.
Instead of each object within the array being different, they’re all the same.
确实.它们都是同一个对象.
您的request
function确实会出于任何原因使用其参数来解决其返回的诺言.当您将相同的tests [id]对象传递给批处理的所有请求时,它们都将以该对象结束.
您的console.dir确实显示了预期的结果,因为请求确实改变了它的参数-测试对象在每次调用之后包含不同的值,这些值随后被记录,然后在下一个调用中被覆盖.
您应该更改cb来创建一个新对象,而不是更改req:
function cb(response) {
let result = {
status: response.statusCode
};
response.on('data', (chunk) => {
if (!ttfb) ttfb = Date.now() - start;
});
response.on('end', () => {
result.ttfb = ttfb;
result.end = Date.now() - start;
resolve(result);
});
response.on('error', (err) => {
result.error = err;
resolve(result);
});
}