Обработка список пар элементов, с помощью обещаний и функциональное программирование


Я опубликовал ответ на StackOverflow , что я считал, чтобы быть приверженным принципам функционального программирования.

Однако, мне сказали, что оригинал Аскер, что это был не "функциональная", как моя функция используется внутренняя переменная oldData которые отслеживали результаты.

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

Функция process() нарушение принципов функционального программирования? Если это так, как бы мне это исправить?

var items = [
    ["item1", "item2"],
    ["item3", "item4"],
    ["item5", "item6"],
    ["item7", "item8"],
    ["item9", "item10"]
]

function action(item) {
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve(item + ":processed");
        }, 100)
    });
}

function process(items) {
  return items.reduce((m, d) => {
    const promises = d.map(i => action(i));
    let oldData;
    return m.then((data) => {
        oldData = data;
        return Promise.all(promises);
      })
      .then(values => {
        //oldData.push(...values);
        oldData.push.apply(oldData, values);
        return Promise.resolve(oldData);
      })
  }, Promise.resolve([]))
}

process(items).then(d => console.log(d))

//Prints:

// ["item1:processed","item2:processed","item3:processed","item4:processed","item5:processed","item6:processed","item7:processed","item8:processed","item9:processed","item10:processed"]

Оригинал Аскер предложил мне обновить мой код, чтобы использовать concat вместо push создавать неизменяемые массивы каждый раз, чтобы сделать это правильно функционируют. Имеет ли это смысл?



Комментарии
2 ответа


Это функция разрушить парадигму функционального программирования в JS?

Короткий ответ: Это зависит

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

// All return an array with numbers starting from s to e with no
// side-effects and takes all input from args. The first one is
// obviously not "functional" but works just as well as the others.

function range(s, e){
const array = []
for(let i = s; i < e; i++){
array.push(s)
}
return array
}

function range(s, e){
return Array(e - l).fill(null).map((v, i) => s + i)
}

function range(s, e){
return s == e ? [] : [s, ...range(s + 1, e)]
}

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


Похоже, цель process функция пакетной обработки массивов элементов. Это можно сделать с помощью рекурсии. Идея состоит в обработке текущего элемента, то функция concat результаты очередного вызова, который делает то же самое, пока ничего не в массиве. Если вы используете узел или Бабеля с transpile, вы можете использовать async/await для упрощения синтаксиса.

const process = async (i) => {
if (!i.length) return []

const r1 = await Promise.all(i[0].map(action))
const r2 = await process(i.slice(1))

return [...r1, ...r2]
}

Если вы не можете сделать async/awaitвот Расширенная версия с обычной обещаю синтаксис.

function process(i) {
if (!i.length) return Promise.resolve([])

return Promise.all(i[0].map(action)).then(function(r1) {
return process(i.slice(1)).then(r2 => r1.concat(r2))
})
}

2
ответ дан 9 апреля 2018 в 09:04 Источник Поделиться

Я считаю, что ваша версия имеет потенциальную утечку памяти. Учитывая, что вы в состоянии сделать то, что вы сделали с oldDataлюбой может сделать то же самое, например

let x
process(items).then(d => x = d)

Вы можете уйти с созданием копии в итоге, т. е.

function process(xs) {
return xs.reduce(...).then(res => res.slice());
}

Но дело о неизменяемых массивов звучит немного смешно для меня. В качестве массива растет и стоимость использования concat; в то время как push практически постоянное время. Имея состояние и не вытекает - это нормально, в противном случае ваш код работал, так хорошо, глядя, как он, будет иметь низкую производительность.

1
ответ дан 9 апреля 2018 в 12:04 Источник Поделиться