管道运营商订单RXJS Angular 5

时间:2018-03-26 20:43:38

标签: javascript angular rxjs

是我,还是我的经营者的命令不对?在下面的代码中,使用console.logs的两次点击返回相同的值。那不可能是对的吗?

return this.pcs.getAvailableMaterials(currentProduct.id).pipe(
            map(
                result => result.result.data
            ),
            tap(
                x => console.log(x, 'before')
            ),
            map(
                materials => {
                    materials.forEach(material => {
                        material.type = 'material'
                    })
                    return materials
                }
            ),
            tap(
                x => console.log(x, 'after')
            ),
            tap(
                materials => {
                    setState({
                        ...state,
                        availableMaterials: materials
                    })
                }
            )
        )

使用NGXS的Angular 5应用程序。

2 个答案:

答案 0 :(得分:5)

请注意@ samanime的答案是正确。请考虑对原始代码进行以下修改:

return this.pcs.getAvailableMaterials(currentProduct.id).pipe(
        ...
        tap(
            x => console.log(x, 'before')
        ),
        map(
            materials => {
                materials.forEach(material => {
                    material.type = 'material'
                })
                return materials
            }
        ),
        delay(1000), // this line is new
        tap(
            x => console.log(x, 'after')
        ),
        ...
    )

仍然将两个日志视为相同。对于@ samanime的答案是正确的,console.log必须花费一整秒才能执行,但肯定不是这样。

这里的问题是RXJS期望管道功能。也就是说,没有管道函数应该创建在执行该函数之外持久存在的任何副作用。在第一次map调用中,您改变materials数组中的对象,然后返回对相同数组的引用。这是副作用。要解决此问题,您应该从第一次map调用返回 new 数组。类似的东西:

map(
  materials =>
    materials.map(material => { // map instead of forEach makes a new array
      return {...material, type: 'material'} // object spread makes new objects
    })
)

这应该可以满足你的期望。

答案 1 :(得分:1)

是的,他们可以......分类。

它可能与Angular或RXJS完全无关。

相反,它与console.log()的异步性质有关。基本上,由于更改发生在相同的对象上(而不是创建新的),并且发生得如此之快,当它们输出到控制台时,对象是相同的。

这是一个精简的示例:

const o = [{ a: 1 }, { a: 2 }, { a: 3 }];
console.log(o);
o.forEach(a => { a.a = 5; return a; });
console.log(o);

在浏览器中的StackOverflow控制台中,您会看到它们的期望(第一个是1, 2, 3,下一个是5)。但是,打开Chrome的开发者工具并查看一下,您会发现它们都显示了所有5

如果你想看到每个的真实状态,你需要以某种方式将它转换为字符串(或创建对象的克隆)。您可以只使用JSON.stringify()执行此操作,或输出您关心的特定基元。任何使它不是同一个对象的东西最终都会正确显示。

如果数组不是超大,只需使用map()Object.assign()(对于浅层克隆)而不是forEach()来制作新副本就可以了(但是这样做只是在大多数情况下,记录到控制台可能有点过分了。)

const o = [{ a: 1 }, { a: 2 }, { a: 3 }];
console.log(o);
const n = o.map(a => Object.assign({}, a, { a: 5 }));
console.log(n);