ES6 Promise.all() 承诺数组的奇怪解析
ES6 Promise.all() strange resolution of promises array
我有一个片段(如下),它会根据几个参数生成请求。它本质上通过区分每个用户的请求来创建类似于 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()
会 return 一个(承诺)解析为一个包含多个对象的数组——每个对象都是不同的,以反映 [=19] 中定义的每个任务的结果=].
[
[{
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()
,数组被 return 编辑为 Actual Results
(对比 Expected Results
)
(为简洁起见,我们假设 request()
return 是一个承诺)
'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
确实显示了预期的结果,因为 request
确实改变了它的参数 - 测试对象在每次调用后包含不同的值,这些值随后被记录下来,然后在下一次调用中被覆盖。
您应该更改 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);
});
}
我有一个片段(如下),它会根据几个参数生成请求。它本质上通过区分每个用户的请求来创建类似于 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()
会 return 一个(承诺)解析为一个包含多个对象的数组——每个对象都是不同的,以反映 [=19] 中定义的每个任务的结果=].
[
[{
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()
,数组被 return 编辑为 Actual Results
(对比 Expected Results
)
(为简洁起见,我们假设 request()
return 是一个承诺)
'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
确实显示了预期的结果,因为 request
确实改变了它的参数 - 测试对象在每次调用后包含不同的值,这些值随后被记录下来,然后在下一次调用中被覆盖。
您应该更改 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);
});
}