【问题标题】:ES6 Promise.all() strange resolution of promises arrayES6 Promise.all() Promise 数组的奇怪解析
【发布时间】:2016-06-21 18:31:34
【问题描述】:

我有一个 sn-p(如下),它将根据几个参数生成请求。它本质上通过区分每个用户的请求来创建类似于JBehaves 的负载。在大多数情况下,这工作正常。请求的生成按预期工作。然而,使用Promise.all() 的结果并没有像预期的那样工作。这引出了我的问题:

Promise.all() 有问题吗?

在这个问题中,结果的格式可能看起来有点奇怪,但本质上我只是在创建一个用户数组(它本身只是一个请求结果数组)。

实际结果

数组中的每个对象不是不同的,而是完全相同的。在大多数情况下,它似乎是推入数组的最后一个对象(但我还没有完全确认这一点)。这种行为最初让我相信我的 sn-p 中存在范围界定问题,但是我找不到它:(

[
    [{
        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() 会返回一个(承诺)解析为具有多个对象的数组 - 每个对象都不同,以反映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(),则数组将返回为@987654331 @(与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

任何帮助将不胜感激!

【问题讨论】:

  • 能否请您检查console.log("same", values[0] == stats)(代替注释掉的console.dir?在我看来,不是最后一个对象被多次推送,而是同一个对象 i> 被使用了。
  • 请向我们展示您的request 功能。我会押注一个忘记重新初始化的对象。
  • @Bergi 结果是same false; same true; same true; same true;
  • @Sean3z:正如我所猜测的,您的request 函数每次都会为同一个对象返回一个promise(尽管它会改变对象)。给我看它的代码,我会指出错误:-)
  • @Bergi 请求脚本pastebin.com/VBJ6Jrgf

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


【解决方案1】:

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

【讨论】:

  • 你先生,太棒了!我将做一些阅读以完全了解这里出现问题时会发生什么,但是,我非常感谢你!
猜你喜欢
  • 2021-07-24
  • 2016-03-20
  • 1970-01-01
  • 1970-01-01
  • 2018-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多