在立即更新另一个 Promise 中的值后,在 Promise 中使用 from 运算符时,Observable 会获取错误的值

Ita*_*tay 5 async-await rxjs ionic-framework angular capacitor

我有一个observable监听路由变化的:)我还有一个promise可以清除我的本地存储,完成后我立即更改路由,但我的switchMap/switchMapTo内部路由更改可观察到的值得到了旧值..为什么会发生这种情况?

我尝试了很多方法来解决这个问题 - 我发现有两种有效:)但我希望了解它们为什么有效。有人可以帮忙吗?这是关于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;
    }
Run Code Online (Sandbox Code Playgroud)

Mrk*_*Sef 4

急切执行与惰性执行

发生这种情况是因为 Promise 是热切的,而 Observable 是懒惰的。也就是说,可观察量在被订阅之前不会执行任何操作,而承诺将从定义的那一刻起尝试解决自身问题。

为了以最直接的方式解决这个问题,我会这样写:

this.router.events.pipe(
  filter((e: Event): e is NavigationEnd => e instanceof NavigationEnd),
  switchMap(_ => this.storageService.getAsync<IUser>('user'))
);
Run Code Online (Sandbox Code Playgroud)

更新:

当然,不仅异步代码(如 Promise 和 Observables)可以具有急切/惰性求值语义。常规同步代码可能存在相同的问题。由于在同步上下文中理解问题可能更容易,因此我将在下面创建一些示例,在不使用 Promise 或 observables 的情况下探索这一点。


考虑这个不可观察的代码:

在第一个示例中,我们有一个函数addNumbers可以为您完成两个数字相加的工作。它是急切的,因此它将立即完成工作,然后在调用后返回一个值。您可以看到,在第 5 行,c()将等于 8。35在第 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());
Run Code Online (Sandbox Code Playgroud)

在下一个示例中,我们有一个非常相似的函数,但它是惰性的。它会记住您给它的数字,但在有东西调用它之前,它实际上不会添加数字。在这种情况下,需要在以任何方式实际使用和之前c调用。直到第 7 行才将它们相加。c()3553

function addNumbers(a, b){
  return () => a + b;
}

const c = addNumbers(3, 5);

console.log("This should be an 8:", c());
Run Code Online (Sandbox Code Playgroud)

懒惰与急切的后果

考虑这两个例子。如果您了解为什么它们打印不同的值,您就会了解当前的问题:)

示例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());
Run Code Online (Sandbox Code Playgroud)

这里,c.n等于3 + 5因为c之前被评估过a.n被设置为7。这与您在删除用户之前检索用户时的情况相同。同时它并没有看到用户信息发生了变化,因为该值已经被计算出来了。

示例2:

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());
Run Code Online (Sandbox Code Playgroud)

这里, cn 等于 ,7 + 5因为 c 是在设置为 后 求值的。这与您使用 检索用户时相同。这次我们检查评估时刻的值,而不是评估时间。a.n7defera.nc()addNumbers(v1, v2)

看到不同?