是我,还是我的经营者的命令不对?在下面的代码中,使用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应用程序。
答案 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);