在立即更新另一个承诺中的值后,将 from 运算符与承诺一起使用时,Observable 会得到错误的值
Observable gets the wrong value when using the from operator with a promise after immediately updating that value in another promise
我有一个监听路线变化的 observable
:) 我还有一个 promise
可以清除我的本地存储,当它完成后我立即改变路线但是我的 switchMap/switchMapTo
在我的 route-change observable 中获取旧值.. 为什么会这样?
我已经尝试了很多方法来解决这个问题 - 我找到了两个有效的方法 :) 但我希望理解它们为什么有效。有人可以帮忙吗?这是关于 hot/cold observables
的问题吗?也许是 event loop
的东西?我真的不知道:)
this.storageService.clear().then((_) => {
// this was successful!! And it was checked - user is deleted :)
// now change the route and trigger the observable below :)
});
this.router.events.pipe(
filter((e: Event): e is NavigationEnd => e instanceof NavigationEnd)
/*
switchMap in here to get the user!
Options 1 + 2 show that the user info exists!!
Although I know for sure that it was deleted.
Option 3 + 4 work.
*/
// 1) switchMapTo(from(this.storageService.getAsync<IUser>('user'))),
// 2) switchMapTo(this.storageService.getAsync<IUser>('user')),
// 3) switchMap(async (_) => {
// const y = await this.storageService.getAsync<IUser>('user');
// return y;
// }),
// 4) switchMapTo(defer(() => this.storageService.getAsync<IUser>('user'))),
);
// Question edited for extra information - this is the getAsync function
async getAsync<T>(key: string) {
const result: GetResult = await Storage.get({ key });
return JSON.parse(result.value) as T;
}
Eager 与 Lazy Execution
发生这种情况是因为 promises 是热切的,而 observable 是惰性的。也就是说,一个可观察对象在被订阅之前不会做任何事情,而一个承诺将从它被定义的那一刻起尝试解决自己。
为了以最直接的方式解决这个问题,我会这样写:
this.router.events.pipe(
filter((e: Event): e is NavigationEnd => e instanceof NavigationEnd),
switchMap(_ => this.storageService.getAsync<IUser>('user'))
);
更新:
当然,不仅异步代码(如 promises 和 observables)可以具有 eager/lazy 求值语义。常规同步代码可能有相同的问题。由于在同步上下文中可能更容易理解这个问题,我将在下面创建一些示例来探索这个问题,而无需使用 promises 或 observables。
考虑这个不可观察的代码:
在第一个示例中,我们有一个函数 addNumbers
可以为您完成两个数字相加的工作。它很急切,所以它会立即完成工作,然后 return 一旦调用一个值。您可以看到在第 5 行,c()
将等于 8。3
和 5
在第 6 行相加,然后在第 8 行打印
function addNumbers(a, b){
const answer = a + b;
return () => answer;
}
const c = addNumbers(3, 5);
console.log("This should be an 8:", c());
在下一个示例中,我们有一个非常相似的函数,但它是惰性的。它会记住你给它的数字,但它不会实际添加数字,直到有东西调用它。在这种情况下,需要在以任何方式实际使用 3
和 5
之前调用 c()
c
。 5
和 3
直到第 7 行才相加。
function addNumbers(a, b){
return () => a + b;
}
const c = addNumbers(3, 5);
console.log("This should be an 8:", c());
懒惰与急切的后果
考虑这两个例子。如果您理解为什么它们打印出不同的值,您就会理解手头的问题:)
示例 1:
const a = { n: 3 };
const b = { n: 5 };
function addNumbers(v1, v2) {
const answer = { n: v1.n + v2.n };
return () => answer;
}
const c = addNumbers(a, b);
a.n = 7;
console.log('Eager Evaluation:', c());
此处,c.n
等于 3 + 5
,因为 c
在 a.n
设置为 7
之前已求值。这与您在删除用户之前检索用户时的情况相同。它没有看到用户信息在此期间发生了变化,因为已经计算了该值。
例二:
const a = { n: 3 };
const b = { n: 5 };
function addNumbers(v1, v2) {
return () => ({ n: v1.n + v2.n });
}
const c = addNumbers(a, b);
a.n = 7;
console.log('Lazy Evaluation:', c());
这里,c.n 等于 7 + 5
,因为 c 在 a.n
被设置为 7
之后计算 。这与您使用 defer
检索用户时相同。这次我们在计算 c()
时检查 a.n
的值,而不是在计算 addNumbers(v1, v2)
时检查
的值。
看出区别了吗?
一些解释帮助我几次理解 defer
。
函数组合是从内到外执行的。首先是给外部函数的内部参数,然后是外部函数。
function f(x) {
const result = x * 10;
console.log("f: ", x, " -> ", result);
return result;
}
function g(x) {
const result = x + 1;
console.log("g: ", x, " -> ", result);
return result;
}
cosnole.log(f(g(2)))
// g: 2 => 3
// f: 3 => 30
// 30
承诺是急切的。他们不会等待有人订阅。
new Promise(() => console.log("1"));
console.log("2");
1
2
function f(p) {
console.log('f:');
}
f(new Promise(() => console.log("1));
// 1
// f:
from(somePromise)
也是如此:
from(new Promise((resolve) => {
console.log("promise 1");
setTimeout(() => {
console.log("promise 2");
resolve(5);
});
});
console.log("subscribe - before");
from.pipe(tap(x) => {
console.log("tap: ", x);
}).subscribe();
console.log("subscribe - after");
// promise 1
// subscribe - before
// subscribe - after
// promise 2
// tap: 5
from
在幕后做了什么(简体):
function from(promise) {
return new Observable(subscriber => {
promise.then(result => {
subscriber.next(result);
subscriber.complete();
}, (err) => subscriber.error(err));
});
}
from
return 一个在 promise 发出时发出的 observable,但是 promise 本身甚至在 from
开始并创建一个 observable 之前就已经开始工作了。
defer 运算符适用于这些情况。当希望延迟作业并仅在有人订阅时才执行。
defer
在幕后做了什么(简化版):
function defer(workFunction) {
return new Observable(subscriber => {
from(workFunction()).subscribe((result) => {
subscriber.next(result);
});
});
}
只有有人订阅时,才会激活工作功能。
如果我们将 defer
给 switchMapTo
,switchMapTo
将仅在源发出时订阅它。
switchMapTo
简化了幕后工作:
function switchMapTo(workObservable) {
let lastSubs;
return new Observable(subscriber => {
if (lastSubs) {
lastSubs.unsubscribe();
}
lastSubs = workObservable.pipe(
tap(result => subscriber.next(result)),
).subscribe();
});
}
我有一个监听路线变化的 observable
:) 我还有一个 promise
可以清除我的本地存储,当它完成后我立即改变路线但是我的 switchMap/switchMapTo
在我的 route-change observable 中获取旧值.. 为什么会这样?
我已经尝试了很多方法来解决这个问题 - 我找到了两个有效的方法 :) 但我希望理解它们为什么有效。有人可以帮忙吗?这是关于 hot/cold observables
的问题吗?也许是 event loop
的东西?我真的不知道:)
this.storageService.clear().then((_) => {
// this was successful!! And it was checked - user is deleted :)
// now change the route and trigger the observable below :)
});
this.router.events.pipe(
filter((e: Event): e is NavigationEnd => e instanceof NavigationEnd)
/*
switchMap in here to get the user!
Options 1 + 2 show that the user info exists!!
Although I know for sure that it was deleted.
Option 3 + 4 work.
*/
// 1) switchMapTo(from(this.storageService.getAsync<IUser>('user'))),
// 2) switchMapTo(this.storageService.getAsync<IUser>('user')),
// 3) switchMap(async (_) => {
// const y = await this.storageService.getAsync<IUser>('user');
// return y;
// }),
// 4) switchMapTo(defer(() => this.storageService.getAsync<IUser>('user'))),
);
// Question edited for extra information - this is the getAsync function
async getAsync<T>(key: string) {
const result: GetResult = await Storage.get({ key });
return JSON.parse(result.value) as T;
}
Eager 与 Lazy Execution
发生这种情况是因为 promises 是热切的,而 observable 是惰性的。也就是说,一个可观察对象在被订阅之前不会做任何事情,而一个承诺将从它被定义的那一刻起尝试解决自己。
为了以最直接的方式解决这个问题,我会这样写:
this.router.events.pipe(
filter((e: Event): e is NavigationEnd => e instanceof NavigationEnd),
switchMap(_ => this.storageService.getAsync<IUser>('user'))
);
更新:
当然,不仅异步代码(如 promises 和 observables)可以具有 eager/lazy 求值语义。常规同步代码可能有相同的问题。由于在同步上下文中可能更容易理解这个问题,我将在下面创建一些示例来探索这个问题,而无需使用 promises 或 observables。
考虑这个不可观察的代码:
在第一个示例中,我们有一个函数 addNumbers
可以为您完成两个数字相加的工作。它很急切,所以它会立即完成工作,然后 return 一旦调用一个值。您可以看到在第 5 行,c()
将等于 8。3
和 5
在第 6 行相加,然后在第 8 行打印
function addNumbers(a, b){
const answer = a + b;
return () => answer;
}
const c = addNumbers(3, 5);
console.log("This should be an 8:", c());
在下一个示例中,我们有一个非常相似的函数,但它是惰性的。它会记住你给它的数字,但它不会实际添加数字,直到有东西调用它。在这种情况下,需要在以任何方式实际使用 3
和 5
之前调用 c()
c
。 5
和 3
直到第 7 行才相加。
function addNumbers(a, b){
return () => a + b;
}
const c = addNumbers(3, 5);
console.log("This should be an 8:", c());
懒惰与急切的后果
考虑这两个例子。如果您理解为什么它们打印出不同的值,您就会理解手头的问题:)
示例 1:
const a = { n: 3 };
const b = { n: 5 };
function addNumbers(v1, v2) {
const answer = { n: v1.n + v2.n };
return () => answer;
}
const c = addNumbers(a, b);
a.n = 7;
console.log('Eager Evaluation:', c());
此处,c.n
等于 3 + 5
,因为 c
在 a.n
设置为 7
之前已求值。这与您在删除用户之前检索用户时的情况相同。它没有看到用户信息在此期间发生了变化,因为已经计算了该值。
例二:
const a = { n: 3 };
const b = { n: 5 };
function addNumbers(v1, v2) {
return () => ({ n: v1.n + v2.n });
}
const c = addNumbers(a, b);
a.n = 7;
console.log('Lazy Evaluation:', c());
这里,c.n 等于 7 + 5
,因为 c 在 a.n
被设置为 7
之后计算 。这与您使用 defer
检索用户时相同。这次我们在计算 c()
时检查 a.n
的值,而不是在计算 addNumbers(v1, v2)
时检查
看出区别了吗?
一些解释帮助我几次理解 defer
。
函数组合是从内到外执行的。首先是给外部函数的内部参数,然后是外部函数。
function f(x) {
const result = x * 10;
console.log("f: ", x, " -> ", result);
return result;
}
function g(x) {
const result = x + 1;
console.log("g: ", x, " -> ", result);
return result;
}
cosnole.log(f(g(2)))
// g: 2 => 3
// f: 3 => 30
// 30
承诺是急切的。他们不会等待有人订阅。
new Promise(() => console.log("1"));
console.log("2");
1
2
function f(p) {
console.log('f:');
}
f(new Promise(() => console.log("1));
// 1
// f:
from(somePromise)
也是如此:
from(new Promise((resolve) => {
console.log("promise 1");
setTimeout(() => {
console.log("promise 2");
resolve(5);
});
});
console.log("subscribe - before");
from.pipe(tap(x) => {
console.log("tap: ", x);
}).subscribe();
console.log("subscribe - after");
// promise 1
// subscribe - before
// subscribe - after
// promise 2
// tap: 5
from
在幕后做了什么(简体):
function from(promise) {
return new Observable(subscriber => {
promise.then(result => {
subscriber.next(result);
subscriber.complete();
}, (err) => subscriber.error(err));
});
}
from
return 一个在 promise 发出时发出的 observable,但是 promise 本身甚至在 from
开始并创建一个 observable 之前就已经开始工作了。
defer 运算符适用于这些情况。当希望延迟作业并仅在有人订阅时才执行。
defer
在幕后做了什么(简化版):
function defer(workFunction) {
return new Observable(subscriber => {
from(workFunction()).subscribe((result) => {
subscriber.next(result);
});
});
}
只有有人订阅时,才会激活工作功能。
如果我们将 defer
给 switchMapTo
,switchMapTo
将仅在源发出时订阅它。
switchMapTo
简化了幕后工作:
function switchMapTo(workObservable) {
let lastSubs;
return new Observable(subscriber => {
if (lastSubs) {
lastSubs.unsubscribe();
}
lastSubs = workObservable.pipe(
tap(result => subscriber.next(result)),
).subscribe();
});
}