Почему функция Array.reduce не вызывается для элементов, помещаемых в исходный массив при уменьшении? - PullRequest
0 голосов
/ 09 ноября 2018

С учетом следующего кода:

const ports = [null,3001];

function getPort(port)
{ return new Promise((resolve, reject) =>
  { // setTimeout to simulate getting port from OS
    setTimeout(() =>
    { if(port < 3010)
      { return reject();
      }
      return resolve();
    }, 2000);
  });
}

ports.reduce(async (acc, port, index) =>
{ if(!port)
  { return;
  }

  try
  {
    const avail = await getPort(port);
    return port;
  }
  catch(avail)
  { ports.push(++port);
  }
});

Почему функция сокращения вызывается только для элементов null и 3001, а не 3002, хотя в конце reduce вызываетсяМассив ports равен [null, 3001, 3002]?

До завершения последнего вызова Reduce в исходном массиве уже есть новый элемент, так почему он также не вызывается для этого элемента?

Цель кода - попытаться найти доступный порт, начиная с заданного числа и увеличивая его до тех пор, пока мы не найдем его, или пока не достигнем верхнего предела.

Есть другие способы сделать это, но reduce Метод казался самым лаконичным, пока я не попал в этот тупик.

1 Ответ

0 голосов
/ 09 ноября 2018

Потому что вот как это определено :

Диапазон элементов, обрабатываемых reduce, устанавливается перед первым вызовом callbackfn. callbackfn. Если элементы, добавленные в массив после начала приведения к уменьшению, не будут посещены, их значение при передаче callbackfn будет значением в то время reduce посещает их; элементы, которые удаляются после начала вызова reduce и до посещения, не посещаются.

(мой акцент)

Но отдельно, поскольку ваш обратный вызов - это функция async, использующая await на getPort перед отправкой массива, вызов reduce завершается еще до того, как произойдет любой вызов push. reduce выполняет свою работу синхронно , даже если вы передаете ему функцию async. async функции возвращают обещаний (если вы посмотрите, acc при втором обратном вызове будет обещанием).

Цель кода - попытаться найти доступный порт, начиная с заданного номера и увеличивая его до тех пор, пока мы не найдем его, или пока не достигнем верхнего предела.

Простой цикл будет ... проще:

async function findPort(start, max) {
    for (let port = start; port < max; ++port) {
        try {
            await getPort(port);
            return port;
        } catch (e) {
            // keep looking
        }
    }
    throw new Error(`No available port found starting at ${start} and stopping before ${max}`);
}

Live Пример:

function getPort(port) {
    return new Promise((resolve, reject) => { // setTimeout to simulate getting port from OS
        setTimeout(() => {
            if (port < 3010) {
                return reject();
            }
            return resolve();
        }, 200);
    });
}

async function findPort(start, max) {
    for (let port = start; port < max; ++port) {
        try {
            await getPort(port);
            return port;
        } catch (e) {
            // keep looking
        }
    }
    throw new Error(`No available port found starting at ${start} and stopping before ${max}`);
}

(async () => {
    try {
        console.log("Trying 3001 through 3020");
        let port = await findPort(3001, 3020);
        console.log(`Got port ${port}`);
    } catch (e) {
        console.error(`Failed: ${e}`);
    }
    try {
        console.log("Trying 3001 through 3009");
        let port = await findPort(3001, 3009);
        console.log(`Got port ${port}`);
    } catch (e) {
        console.error(`Failed: ${e}`);
    }
})();

... но я не мог понять, для чего был создан массив ports, так что ...

...