Передача аргументов в последовательность промисов с помощью bluebird .reduce

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

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

В итоге я использовал возможности .reduce обещаний Bluebird, чтобы сделать это, но я не могу понять, как я должен передавать аргументы всему этому.

Следующий упрощенный пример резюмирует суть моей проблемы:

const Promise = require('bluebird');

var f = (string) => {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log(string);
            resolve();
        }, 3000);
    })
    .catch(err => {
        console.error(err);
    });
};

var strings = ["hello", "world"];

strings.reduce((current, next) => {
    return f(current).then(f(next));
}, Promise.resolve([]))
.then(() => {
    console.log("All done!");
})
.catch(err => {
    console.error(err);
});

Чего я ожидаю и что мне нужно, чтобы сценарий выполнял обещание последовательно для массива аргументов. Я получаю следующий вывод:

hello // after 3s
world // after 3s
All done! // after 3s

Я хочу:

hello // after 3s
world // after 6s
All done! // after 6s

Я понимаю, что проблема в том, что обещания выполняются, как только они созданы, и что я создаю второй экземпляр одновременно с первым, используя f(next).

Я также знаю, что использование промисов без параметров, то есть f1 с жестко заданным console.log("hello") и f2 с жестко заданным console.log("world") работает как положено при использовании

return f1.then(f2);

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


person SpicySquidInk    schedule 09.04.2018    source источник


Ответы (1)


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

Таким образом, внесение обоих изменений (один вызов и передача функции):

strings.reduce((p, next) => {        // ***
    return p.then(() => f(next));    // *** Main changes here
}, Promise.resolve())                // ***
.then(() => {
    console.log("All done!");
})
.catch(err => {
    console.error(err);
});

Живой пример (с более коротким временем ожидания):

var f = (string) => {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log(string);
            resolve();
        }, 1000);
    })
    .catch(err => {
        console.error(err);
    });
};

var strings = ["hello", "world"];

strings.reduce((p, next) => {        // ***
    return p.then(() => f(next));    // *** Main changes here
}, Promise.resolve([]))              // ***
.then(() => {
    console.log("All done!");
})
.catch(err => {
    console.error(err);
});

Я также переименовал параметр current в p, потому что это не "текущая" запись в strings, а предыдущее значение аккумулятора reduce.


Или с помощью краткой функции стрелки:

strings.reduce((p, next) => p.then(() => f(next)), Promise.resolve())
.then(() => {
// ...
person T.J. Crowder    schedule 09.04.2018
comment
Большое вам спасибо, ваш ответ решает мою проблему: я не совсем понял, что такое аккумулятор по отношению к моим итерациям, ваше объяснение очень помогло! - person SpicySquidInk; 09.04.2018
comment
@SpicySquidInk: Рад, что помог! Примечание: мы могли бы использовать краткую форму стрелочной функции выше: strings.reduce((p, next) => p.then(() => f(next)), Promise.resolve()) (также обратите внимание, что нет необходимости передавать пустой массив в Promise.resolve). - person T.J. Crowder; 09.04.2018