У меня есть массив функций javascript Functs = [f1, f2, f3...]
, и я хочу перебрать массив, последовательно выполняя каждую функцию.
Редактировать: в настоящее время у нас есть функции, вызываемые явно в цикле while(true){f1(a,b,c); f2(b); f3(c)...}
. Однако мы хотели бы сделать этот конвейер расширяемым, чтобы пользователи могли вставлять пользовательские функции в конвейер на любом этапе, предоставляя общедоступный метод для вставки элементов в массив, в результате чего: while(true){f1(a,b,c); f2(b); fCustom(b,c); f3(c)...}
. Итак, редактируемый извне пайплайн. Возможно, есть лучший способ сделать это, но редактируемый набор функций кажется самым простым способом.
Проблема в том, что каждая функция имеет свою сигнатуру и требует разных входных данных, например:
f1 = function(name, address, date) {};
f2 = function(name, age) {};
f3 = function(height);
И функции будут зацикливаться так:
mainFunction() {
let name = something;
let address = something;
let date = something;
....
let height = something;
while(condition == true) {
for(f in Functs) {
f(?????); //Each f() has a different signature?
}
}
}
Как убедиться, что я могу передать необходимые параметры каждой функции? Я думал что-то подобное с объектом параметров и обертыванием функций внутри, но не знаю, насколько это будет чисто. По крайней мере, функции могут сохранять свои уникальные сигнатуры, но это все еще кажется кладжом.
Редактировать: учитывая список значений (имя, возраст, дату и т. д.), каждая функция обрабатывает этот блок данных в последовательном конвейере. Возможно, одна функция проверяет значение age
и, если оно соответствует определенному условию, изменяет date
. Следующая функция будет просматривать функции name
и date
и каким-то образом манипулировать ими, а затем перезаписывать name
результатом. Пока один этап конвейера (возможно, после нескольких циклов) не обнаружит, что все полностью обработано. Таким образом, каждый этап должен иметь возможность изменять эти параметры, чтобы изменения были видны следующей функции в последовательности, то есть:f2 = function(height) {height += 2;}
f3 = function(name, age, height){if (height > age) condition = false;}
mainFunction() {
let params = {
name : something,
address : something,
date : something,
...
height : something
};
f1 = function(params) {
f1Inner = function (params.name, params.address, params.date) {};
}
Functs = [f1, f2, f3...]
while(condition == true) {
for(f in Functs) {
f(params);
}
}
}
Знаете ли вы, что можете использовать специальную переменную arguments (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…) в функции и даже не использовать подпись?
Вы можете использовать деструктурирование. f1 = function({name, address, date}) {}
и так далее.
Нет f2 = (height) {height += 2;}
не изменит ничего вне функции. Можете ли вы поделиться более подробной информацией о том, что вы на самом деле пытаетесь сделать? Это немного похоже на проблему XY.
«каждая функция имеет разную сигнатуру и требует разных входных данных» - тогда лучше всего вообще не помещать их в один и тот же массив.
Можете ли вы добавить правильный пример того, что функции будут делать и возвращать? По крайней мере, для 2 функций. f2 = (height) {height += 2;}
ничего не делает. Он просто увеличивает параметр
@VLAZ Это для обработки большого блока данных в конвейере после последовательности шагов. Каждая функция обеспечивает определенный шаг конвейера, и в настоящее время у нас есть функции, вызываемые явно в цикле while(true){f1(a,b,c); f2(b); f3(c)...}
. Однако мы хотели бы сделать этот конвейер расширяемым, чтобы пользователи могли вставлять пользовательские функции в конвейер на любом этапе, предоставляя общедоступный метод для вставки элементов в массив, в результате чего: while(true){f1(a,b,c); f2(b); fCustom(b,c); f3(c)...}
. Я надеюсь, что это проясняет для вас.
@adiga Смотрите мою правку.
Я бы изменил функции, чтобы они принимали один аргумент в качестве объекта, который деструктурируется, чтобы все функции можно было вызывать с одним и тем же аргументом:
mainFunction() {
let name = something;
let address = something;
let date = something;
....
let height = something;
const paramsObj = { name, address, date, height };
for (const fn of Functs) {
fn(paramsObj);
// do whatever you need to do with the return value or loop logic
}
и
f1 = function({ name, address, date }) {};
f2 = function({ name, age }) {};
f3 = function({ height });
Если вам нужно, чтобы эти параметры также были изменены при передаче, вы можете вместо этого деструктурировать в первой строке, что позволит вам изменить объект params, например:
f1 = function(params) {
const { name, address, date } = params;
if (someCondition) {
params.height += 2;
}
};
и тогда последующие вызовы увидят измененный параметр.
OP только что внес правку, которая, кажется, делает этот подход недействительным. Думаю, здесь нам нужно больше информации.
Это по-прежнему действует, если OP использует lodash.cloneDeep (если он вложенный) или Object.assign (если мелкий) перед передачей объекта параметров в функцию. JSON.decode(JSON.stringify(options)) будет работать вместо lodash, если все параметры являются простыми объектами, массивами или примитивами.
@CharlesBamford Я думаю, что OP хочет сохранить предыдущие значения при выполнении следующей функции. Итак, требуется мутация объекта.
const if_then = (if_fn, then_fn) => (params_object) =>
if_fn(params_object) ? then_fn(params_object) : null;
const apply_params = (fn, arg_keys, write_to_key = false) => (params_object) => {
const result = fn(...arg_keys.map((v) => params_object[v]));
if (write_to_key)
params_object[write_to_key] = result;
return result;
};
const name_and_age = (name, age) => name + ' is ' + age + ' and a half years old';
const volume = (height, width, depth) => height * width * depth;
const double_height = (height) => height * 2;
const double_width = (width) => width * 2;
const count_loop = (params_object) => Object.assign(params_object, { loop_count: (params_object.loop_count ?? 0) + 1 })
let functs = [
count_loop,
apply_params(double_height, ['height'], 'height'),
if_then(
(p) => !p.name_and_age,
apply_params(name_and_age, ['name', 'age'], 'name_and_age'),
),
];
const bob = true;
if (bob)
functs.push(
if_then(
(p) => p.loop_count > 2,
apply_params(double_width, ['width'], 'width'),
)
);
functs = functs.concat([
apply_params(volume, ['height', 'width', 'depth'], 'volume'),
console.info
]);
let params = {
name : 'Orbold the Grim',
age : 145,
height: 2,
width: 0.3,
depth: 0.2,
};
const should_exit_loop = (params_object) =>
(params_object.loop_count || 0) > 10 || params_object.height > 8;
while(!should_exit_loop(params)) {
functs.forEach(
(f) => f(params)
);
};
Умно, но на данный момент, почему бы просто не вызывать функции по отдельности?
Вероятно, вы можете заменить сокращение на params_list.map(p => params_object[p])
@adiga о да ... спасибо, я обновил его.
После большого количества тестов и бенчмаркинга самый быстрый метод, который мы придумали, это (используя решение @certainPerformance в качестве отправной точки):
array.some()
. Несколько быстрее, чем for of
или for in
, и даже немного быстрее, чем array.map
.function.call()
для вызова функций. Это позволяет использовать переменную this
, например, если вам нужен доступ к другим функциям класса. Если вы сделаете это, вы также должны передать this
в array.some()
, что сохранит контекст this
без необходимости bind(this)
, что казалось довольно медленным.params
, который дает общую сигнатуру функции, позволяя пользовательским функциям получать доступ к любым соответствующим переменным. Параметры, общие для всех функций, передаются напрямую (для скорости, особенно при изменении этого значения).//=== List of Pipeline Steps ===//
this.pipelineTasks = [
{ name: 'taskA', func: this.taskA },
{ name: 'taskB', func: this.taskB },
{ name: 'taskC', func: this.taskC }
];
// TaskA
TaskA(src, params) {
return src + params.title;
}
// TaskB
TaskB(src, params) {
return params.name ? params.name : params.name + "chicken";
}
// TaskC
TaskC(src, params) {
params.age = params.age + params.shoeSize;
return params.age > params.shoeSize ? params.age : params.shoeSize;
}
//=== Execute the processing pipeline ===//
processPipeline(src, name, age, shoeSize, title) {
const myParams = {
name : name,
age : 10,
shoeSize : 20,
title : title
};
while (myParams.age < 100) {
this.pipelineTasks.some(function(fn) {
return result = fn.func.call(this, src, myParams);
}, this));
//Alternatively, with arrow functions
// (slightly slower in Chrome, significantly slower in Node with hundreds
// of function calls, but doesn't require 'this' in array.some )
// this.pipelineTasks.some((fn) => result = fn.func.call(this, src, myParams));
}
}
Где и как получить параметры для каждой функции?