присвоение массиву в разное время дает разные результаты

У меня есть эти два разных кода, которые немного отличаются, но дают разные результаты:

(async () => {
  const results = new Array(concurrentBrowsers).fill({});
  const browsers = [];
  for (let index = 0; index < concurrentBrowsers; index++) {
    browsers.push(
      (async function() {
        let i = index;
        const browser = await puppeteer.launch({ headless });
        const page = await getNewPage({ browser });
        results[i].loginPage = await timer(actions.loginPage, { page }); // <---- this line
        results[i].homePage = await timer(actions.homePage, {
          page,
          username,
          password
        });
        console.log(i, results[i]);
        browser.close();
      })()
    );
  }
  await Promise.all(browsers);
})();

Что приводит к

1 { loginPage: 2.924, homePage: 19.939 }
2 { loginPage: 2.924, homePage: 21.388 }
3 { loginPage: 2.924, homePage: 21.122 }
0 { loginPage: 2.924, homePage: 21.305 }
4 { loginPage: 2.924, homePage: 21.6 }

И еще один:

(async () => {
  const results = new Array(concurrentBrowsers).fill({});
  const browsers = [];
  for (let index = 0; index < concurrentBrowsers; index++) {
    browsers.push(
      (async function() {
        let i = index;
        const browser = await puppeteer.launch({ headless });
        const page = await getNewPage({ browser });
        let loginPageTime = await timer(actions.loginPage, { page }); // <---- this line
        let homePageTime = await timer(actions.homePage, {
          page,
          username,
          password
        });
        results[i].loginPage = loginPageTime;
        results[i].homePage = homePageTime;
        console.log(i, results[i]);
        browser.close();
      })()
    );
  }
  await Promise.all(browsers);
})();

Что приводит к

2 { loginPage: 3.291, homePage: 17.911 }
4 { loginPage: 3.226, homePage: 18.949 }
1 { loginPage: 3.047, homePage: 22.619 }
0 { loginPage: 3.291, homePage: 24.508 }
3 { loginPage: 3.059, homePage: 26.391 }

В первом коде есть ошибка, кажется, что результат loginPage всегда берется из последней итерации и, как вы можете видеть, устанавливается на одно и то же значение во всех итерациях.

Единственная разница в том, что я присваиваю результат таймера в первом примере results[i].loginPage, а во втором примере loginPageTime.

Может кто-нибудь объяснить эту разницу?

Спасибо


person Michael    schedule 21.08.2017    source источник
comment
Между прочим, асинхронные функции — это функция ES2017.   -  person Felix Kling    schedule 21.08.2017


Ответы (1)


На самом деле оба варианта кода имеют одну и ту же проблему, но второй блок кода не показывает, что происходит не так:

const results = new Array(concurrentBrowsers).fill({});

Это создает один(!) пустой объект и назначает его всем элементам массива. Все, что вы измените в этом объекте, будет видно во всех элементах массива, поскольку все они ссылаются на один и тот же объект.

Причина, по которой вы видите несоответствие, происходящее в первой версии, заключается в том, что между двумя выражениями await, которые находятся в одной итерации, происходят другие присваивания, в то время как во второй версии кода присваивание выполняется только непосредственно перед console.log.

Вы можете выполнить правильную инициализацию results следующим образом:

const results = Array.from(new Array(concurrentBrowsers), _ => ({}));
person trincot    schedule 21.08.2017
comment
@Michael Это один и тот же объект в каждом элементе массива. не это ценности. - person Keith; 21.08.2017
comment
@Keith Если это так, то почему loginPage такой же, а homePage - нет? - person Bob; 21.08.2017
comment
@Bob Поскольку запросы являются асинхронными и помещаются внутри Promise.All, .. И затем он записывает консоль, когда он идет, это вопрос времени. Все его результаты будут зависеть от того, когда обещания будут разрешены. Если бы он сделал console.log(results) после своего Promise.all, вы бы увидели все значения одинаковыми. - person Keith; 21.08.2017
comment
@trincot это умно и действительно было проблемой. Я до сих пор не понимаю, почему результаты были разными в двух случаях - person Michael; 21.08.2017
comment
Возможно, это поможет понять, если в первой версии кода (с проблемной инициализацией results) вы поместите console.log(i, results[i]) между двумя ожиданиями: тогда вы заметите, что это зарегистрированное значение в целом не совпадает с выводом, который вы получаете от console.log, который следует за несколькими строками ниже (для того же i). Это связано с тем, что второй await позволит разрешать другие промисы, а также назначит loginPage, что заменит значение, которое вы только что зарегистрировали. - person trincot; 21.08.2017