Подтвердить что ты не робот

Является ли Node.js собственная обработка Promise.all параллельной или последовательной?

Я хотел бы прояснить этот момент, поскольку документация не слишком ясна об этом;

Q1: Является ли Promise.all(iterable) обрабатывать все promises последовательно или параллельно? Или, точнее, это эквивалент запуска прикованного promises как

p1.then(p2).then(p3).then(p4).then(p5)....

или это какой-то другой алгоритм, в котором все p1, p2, p3, p4, p5 и т.д. вызывают одновременно (параллельно) и результаты возвращаются как только все разрешится (или отклоняется)?

Q2: Если Promise.all работает параллельно, существует ли удобный способ запуска последовательного последовательного преобразования?

Примечание. Я не хочу использовать Q или Bluebird, но все собственные спецификации ES6.

4b9b3361

Ответ 1

Выполняется Promise.all(iterable) выполнение всех promises?

Нет, promises не может быть выполнен ". Они начинают свою задачу, когда они создаются - они представляют только результаты - и вы выполняете все параллельно, прежде чем передавать их в Promise.all.

Promise.all ожидает только несколько promises. Не важно, в каком порядке они решаются, или параллеливаются ли вычисления.

Есть ли удобный способ запускать итерабельную последовательность?

Если у вас уже есть promises, вы не можете сделать многое, кроме Promise.all([p1, p2, p3, …]) (который не имеет понятия последовательности). Но если у вас есть итерируемые асинхронные функции, вы можете запустить их последовательно. В основном вам нужно получить от

[fn1, fn2, fn3, …]

к

fn1().then(fn2).then(fn3).then(…)

и решение для этого использует Array::reduce:

iterable.reduce((p, fn) => p.then(fn), Promise.resolve())

Ответ 2

В параллели

await Promise.all(items.map(async item => { await fetchItem(item) }))

Преимущества: Быстрее. Все итерации будут выполняться, даже если не удастся.

В последовательности

for (let i = 0; i < items.length; i++) {
    await fetchItem(items[i])
}

Преимущества: переменные в цикле могут совместно использоваться каждой итерацией. Ведет себя как нормальный императивный синхронный код.

Ответ 3

Бергис ответил мне на правильный трек, используя Array.reduce.

Однако, чтобы фактически получить функции, возвращающие мой promises для выполнения один за другим, мне пришлось добавить еще несколько гнезд.

Мой реальный прецедент - это массив файлов, которые мне нужно передавать по порядку один за другим из-за ограничений ниже по течению...

Вот что я закончил.

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(() => {
            return transferFile(theFile); //function returns a promise
        });
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});

Как следует из предыдущих ответов, используя:

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(transferFile(theFile));
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});

не дождался завершения передачи перед запуском другой, а также текст "Все файлы переданы" до того, как была запущена первая передача файлов.

Не уверен, что я сделал не так, но хотел поделиться тем, что сработало для меня.

Изменить: Поскольку я написал этот пост, я теперь понимаю, почему первая версия не работала. то() ожидает, что функция вернет обещание. Итак, вы должны передать имя функции без круглых скобок! Теперь моя функция требует аргумента, поэтому мне нужно обернуть анонимную функцию без аргументов!

Ответ 4

просто уточнить ответ @Bergi (что очень красно, но сложно понять;)

Этот код будет запускать каждый элемент в массиве и добавлять следующую "цепочку" до конца;

function eachorder(prev,order) {
        return prev.then(function() {
          return get_order(order)
            .then(check_order)
            .then(update_order);
        });
    }
orderArray.reduce(eachorder,Promise.resolve());

Надеюсь, что это имеет смысл.

Ответ 5

Вы также можете обрабатывать итерацию последовательно с помощью функции async с использованием рекурсивной функции. Например, заданный массив a для обработки с асинхронной функцией someAsyncFunction():

var a = [1, 2, 3, 4, 5, 6]

function someAsyncFunction(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("someAsyncFunction: ", n)
      resolve(n)
    }, Math.random() * 1500)
  })
}

//You can run each array sequentially with: 

function sequential(arr, index = 0) {
  if (index >= arr.length) return Promise.resolve()
  return someAsyncFunction(arr[index])
    .then(r => {
      console.log("got value: ", r)
      return sequential(arr, index + 1)
    })
}

sequential(a).then(() => console.log("done"))

Ответ 6

Вы можете сделать это для цикла.

Функция возврата функции async

async function createClient(client) {
    return await Client.create(client);
}

let clients = [client1, client2, client3];

если вы пишете следующий код, тогда клиент создается параллельно

const createdClientsArray = yield Promise.all(clients.map((client) =>
    createClient(client);
));

тогда все клиенты создаются параллельно. но если вы хотите создать клиент последовательно, то вы должны использовать для цикла

const createdClientsArray = [];
for(let i = 0; i < clients.length; i++) {
    const createdClient = yield createClient(clients[i]);
    createdClientsArray.push(createdClient);
}

тогда все клиенты создаются последовательно.

счастливое кодирование:)

Ответ 7

Я использовал для решения последовательного promises. Я не уверен, что это помогает здесь, но это то, что я делал.

async function run() {
    for (let val of arr) {
        const res = await someQuery(val)
        console.log(val)
    }
}

run().then().catch()

Ответ 8

это может ответить на часть вашего вопроса.

да, вы можете связать массив функций, возвращающих обещание, следующим образом... (это передает результат каждой функции в следующую). вы могли бы, конечно, отредактировать его, чтобы передать один и тот же аргумент (или без аргументов) каждой функции.

function tester1(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a + 1);
    }, 1000);
  })
}

function tester2(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a * 5);
    }, 1000);
  })
}

function promise_chain(args, list, results) {

  return new Promise(function(done, errs) {
    var fn = list.shift();
    if (results === undefined) results = [];
    if (typeof fn === 'function') {
      fn(args).then(function(result) {
        results.push(result);
        console.log(result);
        promise_chain(result, list, results).then(done);
      }, errs);
    } else {
      done(results);
    }

  });

}

promise_chain(0, [tester1, tester2, tester1, tester2, tester2]).then(console.log.bind(console), console.error.bind(console));

Ответ 9

Ответ Берги помог мне сделать вызов синхронным. Я добавил ниже пример, где мы вызываем каждую функцию после вызова предыдущей функции.

function func1 (param1) {
    console.log("function1 : " + param1);
}
function func2 () {
    console.log("function2");
}
function func3 (param2, param3) {
    console.log("function3 : " + param2 + ", " + param3);
}

function func4 (param4) {
    console.log("function4 : " + param4);
}
param4 = "Kate";

//adding 3 functions to array

a=[
    ()=>func1("Hi"),
    ()=>func2(),
    ()=>func3("Lindsay",param4)
  ];

//adding 4th function

a.push(()=>func4("dad"));

//below does func1().then(func2).then(func3).then(func4)

a.reduce((p, fn) => p.then(fn), Promise.resolve());

Ответ 10

С помощью async await массив обещаний можно легко выполнить последовательно:

let a = [promise1, promise2, promise3];

async function func() {
  for(let i=0; i<a.length; i++){
    await a[i]();
  }  
}

func();

Примечание. В вышеприведенной реализации, если обещание отклонено, остальные не будут выполнены. Если вы хотите, чтобы все ваши обещания были выполнены, оберните ваш await a[i](); внутри try catch