ES6 Promise.all()promises数组

时间:2016-03-08 00:24:49

标签: javascript node.js promise ecmascript-6 bluebird

我有一个片段(下面)会根据几个参数生成请求。它基本上通过区分每个用户的请求来创建类似于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)解析为具有多个对象的数组 - 每个对象都不同,以反映tasks()中定义的每个任务的结果。

[
    [{
        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 Results(与Expected Results

(为简洁起见,我们假设request()返回一个承诺)

'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

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:3)

  

Promise.all()是否存在问题?

没有。

  

而不是数组中的每个对象都不同,它们都是一样的。

事实上。它们都是同一个对象。

你的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);
  });
}