Самый быстрый способ обработки вложенного дерева обещаний в JavaScript? - PullRequest
0 голосов
/ 25 февраля 2019

Я пытаюсь найти самый быстрый способ обработки вложенного дерева заданий, которые возвращают обещания в javascript.У меня есть следующие условия:

  1. Процесс должен возвращать обещание, которое разрешается только после завершения всех вложенных заданий.
  2. Каждое задание на одном уровне необходимо обрабатывать впоследовательность, но потомки могут обрабатываться параллельно.
  3. Я не могу использовать await / async.

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

    function sumNumbers(numbersToSum) {
        return numbersToSum.reduce((p, current) => {
            return p.then(runningTotal => {
                return fetch(`http://numbersapi.com/${current.number}/`)
                    .then(result => {
                        var parentTotal = runningTotal + current.number;
                        if (current.children && current.children.length > 0) {
                            return sumNumbers(current.children)
                                .then(childrenTotal => {
                                    return childrenTotal + parentTotal;
                                });
                        }
                        else {
                            return parentTotal;
                        }
                    });
            });
        }, Promise.resolve(0));
    }

    var numbers = [
        {
            number: 2,
            children: [
                { number: 1 },
                {
                    number: 3,
                    children: [
                        { number: 2 },
                        {
                            number: 1,
                            children: [
                                { number: 1 },
                                { number: 2 }
                            ]
                        }
                    ]
                },
                { number: 2 }
            ]
        },
        { number: 4 },
        { number: 5 },
        {
            number: 3,
            children: [
                {
                    number: 1,
                    children: [
                        { number: 1 }
                    ]
                },
                { number: 2 }
            ]
        }
    ];

    (() => {
        var startTime = new Date();
        sumNumbers(numbers)
            .then(total => {
                var finishTime = new Date();
                console.log(`${total} (took ${((finishTime - startTime) / 1000)}s)`);
            });
    })();

Когда я запускаю его в своей веб-консоли, я получаю следующий результат:

30 (took 2.839s)

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

Это так?Если да, то как бы вы переписали пример, чтобы воспользоваться этим подходом?Если нет, есть ли более быстрый способ сделать это в целом?

1 Ответ

0 голосов
/ 26 февраля 2019

Было бы быстрее разрешить родителя сразу же после запуска дочерней работы, а затем суммировать результат позже?

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

Или, если быть более точным, первая выборка с первого уровня выполняется одновременно с первой выборкой со второго, третьего и четвертого.Это 4 запроса, выполняемые параллельно, а не последовательно (как в примере кода в операторе), что, безусловно, сэкономит значительное время при масштабировании на более крупный пример.

Да, это будет быстрее,но не существенно - постоянный фактор в большинстве.Вам все равно придется последовательно обрабатывать все листья дерева.

Код для этого тривиален - вы просто замените fetch(node).then(processChildren) на параллельную версию:

return Promise.all([
    fetch(`http://numbersapi.com/${current.number}/`),
    current.children && current.children.length > 0
      ? sumNumbers(current.children)
      : 0
}).then(([result, childrenTotal]) => {
//        ^^^^^^
    var parentTotal = runningTotal + current.number;
    return childrenTotal + parentTotal;
});

Я заметил, что вы на самом деле никогда не используете result, поэтому мне интересно, почему вы вообще что-то получаете: -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...