Pull to refresh

Comments 33

UFO just landed and posted this here

В данном примере результов нет, так как delayedLog просто выводил в консоль. Но если нужны то:


const results = await Promise.all(array.map(delayedLog););
Как получить список ошибок из отклонённых промисов?
После первого же отклоненного промиса, выполнение приостанавливается. Ошибку вы сможете перехватить через try catch.
А как быть, если хочется, чтобы все промисы выполнились даже если один реджектнулся?

Я для этого писал функцию promiseResolveAll, но может быть, есть более хороший способ.

async function promiseResolveAll(promises){
    if(promises.length === 0){
        return [];
    }
    return new Promise(resolve => {
        var results = [];
        var processed = 0;
        function onPromiseAnswer(type, i){
            return value => {
                results[i] = type ? {ok: true, value: value} : {ok: false, error: value};
                if(++processed === promises.length){
                    resolve(results);
                }
            };
        }

        promises.forEach((promise, i) => {
            promise.then(onPromiseAnswer(true, i)).catch(onPromiseAnswer(false, i));
        });
    });
}
Я бы ваш алгоритм как-то так реализовал бы:

const promises = [
  Promise.resolve(1),
  Promise.reject('something wrong'),
  Promise.resolve(3),
];

function fail(error) { 
  return {ok: false, error};
};
function success(value) { 
  return {ok: true, value};
};

const results = await Promise.all(
  promises.map(p => p.then(success).catch(fail))
);
// [{ok: true, value: 1}, {ok: false, error: "something wrong"}, {ok: true, value: 3}];

Только всё-таки p => p.then(success, fail). Это и короче, и семантичнее (мы перехватываем не общую ошибку цепочки p.then(success), а ошибку которая возникает в p).

Каким это таким образом выполнение приостанавливается, когда все промисы — независимые?
Согласен, ввожу в заблуждение.

Само по себе выполнение каждого из промисов продолжается, завершается лишь исполнение Promise.all() при первом же режекте.

Примерно проследить за поведением Promise.all() можно на этом примере:
'use strict';

const sleep = (time, v, err = false) => new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(`start   ${v}, time ${time}`);
        if (err) {
            console.log(`reject  ${v}, time ${time}`);
            return void reject(new Error(`REJECTED: ${v}`));
        }

        console.log(`resolve ${v}, time ${time}`);
        resolve(`RESOLVED ${v}`);
    }, time * 1000);
});


const promises = [
    sleep(2, '1'),
    sleep(1, '2'),
    sleep(2, '3'),
    sleep(4, '4', true),
    sleep(3, '5'),
    sleep(2, '6'),
    sleep(1, '7', true),
    sleep(5, '8'),
];


console.log('Begin');
Promise.all(promises)
    .then(values => {
        console.log(JSON.stringify(values, null, 4));
    })
    .catch(err => {
        console.log(err);
    })
    .then(() => {
        console.log('* Promise.all finished *');
    });
console.log('End');


Результат исполнения будет примерно таким:
% node test.js
Begin
End
start   2, time 1
resolve 2, time 1
start   7, time 1
reject  7, time 1
Error: REJECTED: 7
* Promise.all finished *
start   1, time 2
resolve 1, time 2
start   3, time 2
resolve 3, time 2
start   6, time 2
resolve 6, time 2
start   5, time 3
resolve 5, time 3
start   4, time 4
reject  4, time 4
start   8, time 5
resolve 8, time 5
const results = await Promise.all(
    array
      .map(delayedLog)
      .map(promise => promise.catch(err => new SynteticError(e))));

const withError = result.filter(e => e instanceof SynteticError);

Некрасиво, да. try… catch тоже перестанет работать, потому что все ошибки будут обработаны. Но можно сделать обертку вроде promise.Any, который будет кидать массив exception.

await array.reduce((accumulator, item) => accumulator.then(item)), Promise.resolve());
Вот не надо писать такого в туториалах, кто-то же может и запомнить!

Трюк с reduce имел смысл только до появления await.
По вашей ссылке написано, что следует избегать await в цикле, потому что цикл от такого становится последовательным, хотя мог бы быть параллельным.

Но reduce точно так же делает все итерации последовательными.
Не понятно, в чем проблема использовать классический for, который, так случилось, с await'ами заходит лучше всего.
for (var i=0; i < array.length; i++) {
    await func(array[i])
}


Не модно? Смузями заплюют? Замените var на let — пусть подавятся.

Абсолютно согласен. Если хочется быть модным-функциональным можно последовательно в редьюсе обрабатывать промисы

Это то же самое, что пример под номером 2 в статье, только с дополнительной переменной. Если вам нужна последовательная обработка каждого элемента массива, то используйте
Поддерживаю. Без for await статья не полна

По моему это мертворожденная бесполезная хрень. Не знаю не 1 применения когда стоит её применять, точнее всё случаи когда стоит лучше переписать по другому.

Как вы в таком случае предлагаете читать большой файл под нодой? Варианта как бы только два: либо for await, либо подписка на событие data.


Но в комментарии ниже вы написали, что вам не нравится комбинация }) — значит, остаётся только for await.

Не настолько не нравится, просто нужно стараться избегать, разрабы всё больше и больше дают инструментов для этого.
В идеале обрабатывать в стиме там да без колбеков некуда.

Стоп, сувать в forEach анонимную функцию когда есть просто for имхо извращение. ForEach используют когда пишут в функциональном стиле.
Это не говоря о том что фор более гибкий, можно закешировать длину массива ускорив на 10% выполнение, можно через 1 перебирать и тд.
Да и потом чем мешьше вложеных скобок тем читабельнее код, вот от такого


});

нужно избавлсятся

А какие ещё варианты вы предлагаете для случая, когда итерации цикла должны быть параллельными?

Если нужно обработать момент когда всё итерации разрешатся, то вариант автора статьи единственный адекватный.

«Вариант автора статьи» — это который? Тут как бы приведено 3 разных варианта, каждый для своего случая.
когда итерации цикла должны быть параллельными

Вы сами спросили про третий случай по нумерации автора, только через промис олл по другому это костыли типа while(1) и проверять всё итерации на предмет завершения.

Вариант 3 — это когда итерации должны быть параллельными и требуется отследить завершение. А если отслеживать завершение не требуется — он превращается в вариант 1.
async function processArray(array) {
  array.forEach(async (item) => {
    await func(item);
  })
  console.log('Done!');
}

Огромное спасибо за статью, без нее тупил бы полдня, не понимая, почему не срабатывает await, у меня ровно такой случай!
Sign up to leave a comment.

Articles