就像我们在Java流中执行流水线一样,如何在JavaScript中执行操作?

时间:2019-02-07 07:11:49

标签: javascript java

在使用流的Java 8中,当我一个接一个地链接方法时,以流水线方式执行操作。

示例:

List<Integer> nums = Arrays.asList(1,2,3,4,5,6);    
nums.stream().map(x->{
    x = x * x;
    System.out.println("map1="+x);
    return x;
}).map(x->{
    x = x * 3;
    System.out.println("map2="+x);
    return x;
}).forEach(x-> System.out.println("forEach="+x));

输出:-

map1=1
map2=3
forEach=3
map1=4
map2=12
forEach=12
map1=9
map2=27
forEach=27
map1=16
map2=48
forEach=48
map1=25
map2=75
forEach=75
map1=36
map2=108
forEach=108

但是当我在javascript中尝试类似的情况时,结果却不同。与javascript中一样,第一个操作完成了,然后执行了第二个操作。示例:-

var nums = [1,2,3,4,5,6 ];
nums.map(x => {
  x = (x * x);
  console.log('map1='+x);
  return x;})
  .map(x => {
  x = x * 3;
  console.log('map2='+x);
  return x;})
  .forEach(x=> console.log('forEach='+x));

输出:-

 map1=1
 map1=4
 map1=9
 map1=16
 map1=25
 map1=36
 map2=3
 map2=12
 map2=27
 map2=48
 map2=75
 map2=108
 forEach=3
 forEach=12
 forEach=27
 forEach=48
 forEach=75
 forEach=108

JavaScript中是否有任何方法可以使其以管道方式执行操作,并且获得Java程序中的输出?

This question ask only how to collect like in JavaScript but not how internal working changes for same type of methods

7 个答案:

答案 0 :(得分:18)

也许以后(或永远不会),您可以使用具有以下语法的实际实验 pipeline operator |>

expression |> function

将功能作为单独的功能并为每个管道迭代流数组可以实现您想要的结果。

这仅在FF中有效。从58版开始:此功能位于--enable-pipeline-operator编译标志的后面。

const
    a = x => { x = x * x; console.log("map1=" + x); return x; },
    b = x => { x = x * 3; console.log("map2=" + x); return x; },
    c = x => console.log("forEach=" + x)

var nums = [1, 2, 3, 4, 5, 6];

nums.forEach(v => v |> a |> b |> c);

与管道作为函数(function composition enabling piping)相同,并关闭了所需的函数。

const
    pipe = (...functions) => input => functions.reduce((acc, fn) => fn(acc), input),
    a = x => { x = x * x; console.log("map1=" + x); return x; },
    b = x => { x = x * 3; console.log("map2=" + x); return x; },
    c = x => console.log("forEach=" + x)

var nums = [1, 2, 3, 4, 5, 6],
    pipeline = pipe(a, b, c);

nums.forEach(pipeline);

答案 1 :(得分:12)

如果将每个函数操作放入一个数组中,则可以使用reduce遍历该数组,并将最后的计算值传递到累加器中,直到到达函数数组的末尾为止。

var nums = [1,2,3,4,5,6 ];
var fns = [
  (x) => {
    x = x * x;
    console.log('map1=' + x);
    return x;
  },
  (x) => {
    x *= 3;
    console.log('map2=' + x);
    return x;
  },
  (x) => {
    console.log(x);
    return x;
  }
];

nums.forEach((num) => {
  fns.reduce((lastResult, fn) => fn(lastResult), num);
  // pass "num" as the initial value for "lastResult",
  // before the first function has been called
});

您不能使用nums.map,因为.map在解析为映射的输出数组之前必须遍历整个输入数组(此后映射的输出数组将有另一个.map调用)。

答案 2 :(得分:2)

与Java的流等效的是JavaScript的iterators。不幸的是,迭代器对象没有map方法(yet),但是您可以自己编写一个对象(如果需要方法语法,甚至可以install it on the prototype)。

function* map(iterable, f) {
    for (var x of iterable)
        yield f(x);
}

var nums = [1,2,3,4,5,6];
function square(x) {
  x = (x * x);
  console.log('map1='+x);
  return x;
}
function triple(x) {
  x = x * 3;
  console.log('map2='+x);
  return x;
}
for (const x of map(map(nums.values(), square), triple)) {
  console.log('forEach='+x);
}

还要注意,在函数式编程中,顺序对于纯操作无关紧要-如果使用map,则不必依赖执行顺序。

答案 3 :(得分:1)

当我们有解决方案时,为什么要从头开始重新发明。此功能存在于 lodash / RxJS / stream.js 中。

lodash的示例片段:

_.flow(
 _.assign(rows[0]),
 _.omit('blah')
)(foundUser);

// >> {"charData":[],"ok": 1}

但是,javascript在单线程上运行,这些库也是如此。 Java流受益于多核系统(在并行情况下)。在那里,他们可以使用多个线程来使用所有可用的内核。

答案 4 :(得分:0)

我建议使用RxJS之类的库。无论是顺序处理,并行处理还是任何处理,这都使您可以更好地控制处理类型。

这里是一个示例,与您的预期很接近:

const source = Rx.Observable.from([{name: 'Joe', age: 30}, {name: 'Frank', age: 20},{name: 'Ryan', age: 50}]);
const example = source.map(person => {
  console.log("Mapping1" + person.name)
  return person.name
});
const subscribe = example.subscribe(val => console.log(val));

输出:

"Mapping1Joe"
"Joe"
"Mapping1Frank"
"Frank"
"Mapping1Ryan"
"Ryan"

答案 5 :(得分:0)

您在此处使用的JS代码没有任何懒惰求值的概念-.map()的返回值已经是一个数组值,必须先对其进行完全求值,然后才能使用自己的.map()方法。被执行。这不是解释器如何操作的实现细节,而是语言定义的一部分。 (同时,Java代码的Collection.stream()方法返回一个Stream对象,其内容尚未评估。)

JavaScript确实具有您想要的 Promise 对象形式的异步/延迟评估功能。

以下代码将执行您想要的操作:

var nums = [1,2,3,4,5,6 ];
nums.map(async function(x) {
  x = (x * x);
  console.log('map1='+x);
  return x;
}).map(async function(x) {
  x = await x;
  x = x * 3;
  console.log('map2='+x);
  return x;
}).forEach(async function(x) {
  x = await x;
  console.log('forEach='+x);
});

现在,实际上,由于Promise会立即解决,因此仍然会按照以前的顺序打印输出。但是,这次映射功能的评估确实是“惰性的”,并且原则上可以以任何顺序进行。为了进行实际测试,我们可以使用异步sleep()函数(来自What is the JavaScript version of sleep()?)在计算中引入延迟:

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

var nums = [1,2,3,4,5,6 ];
nums.map(async function(x) {
  x = (x * x);
  await sleep(1); // 1ms delay
  console.log('map1='+x);
  return x;
}).map(async function(x) {
  x = await x;
  x = x * 3;
  console.log('map2='+x);
  return x;
}).forEach(async function(x) {
  x = await x;
  console.log('forEach='+x);
});

输出:

map1=1
map2=3
forEach=3
map1=4
map2=12
forEach=12
map1=9
map2=27
forEach=27
map1=16
map2=48
forEach=48
map1=25
map2=75
forEach=75
map1=36
map2=108
forEach=108

答案 6 :(得分:-2)

您可以得到相同的输出,因为map1map2forEach值序列的相对值是相同的。

您所看到的顺序差异表明了JVM和JavaScript运行时引擎的机器模型之间的根本差异。

JVM是线程化的。 JavaScript不是。这意味着,在执行关键数量的映射操作后,您的Java中的序列步骤可以立即运行。

在JavaScript中,下一步位于执行堆栈的底部,必须首先执行顶部的每个操作,然后才能进入下一个项目。

如您所见,这些方法在功能上是等效的,但是机制不同。