繁体   English   中英

我如何在JavaScript中执行操作就像在Java流中进行操作管道一样?

[英]How can I perform operations in JavaScript just like we do pipeline of operations in Java streams?

在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程序中获得输出?

这个问题只询问如何在JavaScript中收集,而不是如何在相同类型的方法中更改内部工作

也许以后(或从不)你可以使用实际的实验 管道运算符|> ,它具有以下语法:

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); 

管道作为功能( 功能组合使能管道 )与所需功能的闭合相同。

 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); 

如果将每个函数操作放入一个数组中,则可以使用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 )。

与Java的流相当的是JavaScript的迭代器 迭代器对象遗憾的是没有map的方法( ),但你可以很容易地自己写一个(甚至对样机安装它 ,如果你想方法的语法)。

 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则不需要依赖执行顺序。

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

来自lodash的示例代码段:

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

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

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

我建议使用像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"

你在这里使用的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()函数(来自什么是sleep()的JavaScript版本来引入计算延迟 ):

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

你有点得到相同的输出,因为map1map2forEach值的相对值是相同的。

您所看到的顺序差异显示了JVM的机器模型与JavaScript运行时引擎之间的底层差异。

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

在JavaScript中,下一步放在执行堆栈的底部,并且必须首先执行顶部的每个操作,然后才能到达下一个项目。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM