如果块不等待则在内部等待

问题描述 投票:0回答:1

我正在使用sequelize从数据库表中加载一些数据,并将其缓存在具有TTL的内存对象中,这样我就不会一遍又一遍地从数据库中读取数据。

// ffm.ts
import { Op } from 'sequelize';
import { State } from 'src/db/models/geo/state';

export class FFMStates {
  private static lastRefreshed = new Date(0); // Initialize to start of epoch

  private static TTL = 60 * 60; // 1 hour

  public static DATA: Record<number, Array<string>>;

  public static async load() {
    FFMStates.lastRefreshed = new Date();
    const currentYear = FFMStates.lastRefreshed.getFullYear();

    const results = await State.findAll({
      where: {
        year: { [Op.gte]: currentYear - 2 }, // load 3 or 4 latest years
        ffm: true,
      },
      order: [
        ['year', 'ASC'],
        ['code', 'ASC'],
      ],
    });

    FFMStates.DATA = results.reduce((acc: Record<number, string[]>, state) => {
      if (!acc[state.year]) {
        acc[state.year] = [];
      }
      acc[state.year].push(state.code);
      return acc;
    }, {});
  }

  public static async get() {
    if ((new Date().getTime() - FFMStates.lastRefreshed.getTime()) / 1000 > FFMStates.TTL) {
      await FFMStates.load();
    }
    return FFMStates.DATA;
  }
}

然后我按如下方式使用该模块:

import { FFMStates } from 'src/constants/health/ffm';

const ffmStates = await FFMStates.get();
const currentYear = ffmStates[this.year]; // This throws an error the first time this code runs because ffmStates is undefined

我尝试使用

console.log()
语句进行调试。似乎当第一次调用
FFMStates.get()
时,我的代码开始运行
FFMStates.load()
但不等待它,并立即执行
if block
之后的下一行并返回
FFMStates.DATA
,此时尚未定义。我不知道为什么
await FFMStates.load()
不等。

javascript if-statement asynchronous async-await
1个回答
0
投票

我看不出所提供的代码是如何导致错误的。我只能看到,如果这些调用在执行所需的时间内,则在您第二次(及后续)调用

.get()
时才会发生错误
State.findAll({

即如果

.findAll
需要 1 秒才能得到结果,那么
FFMStates.DATA
此时将是
undefined
,但
FFMStates.lastRefreshed
将已更新。因此,在 1 秒时间段内对
.get()
的任何调用都将返回仍未定义的
.DATA

尝试进行以下更改。

在您的班级中添加

private static inflight: Promise<any> | null = null;

.load
末尾添加

FFMStates.inflight = null;

像这样改变

.get()

public static async get() {
  if (FFMStates.inflight || (new Date().getTime() - FFMStates.lastRefreshed.getTime()) / 1000 > FFMStates.TTL) {
    if (!FFMStates.inflight) {
      FFMStates.inflight = FFMStates.load();
    }
    await FFMStates.inflight;
  }
  return FFMStates.DATA;
}

另一种解决方法是将

FFMStates.DATA
变为
Promise

public static DATA: Promise<Record<number, Array<string>>>;

更改

.load
以返回
results.reduce

return results.reduce((acc: Record<number, string[]>, state: {year:number, code:string}) => {

并更改

.get
如下:

public static async get() {
  if ((new Date().getTime() - FFMStates.lastRefreshed.getTime()) / 1000 > FFMStates.TTL) {
    // DO NOT USE await here
    FFMStates.DATA = FFMStates.load();
  }
  return FFMStates.DATA;
}

可以说更干净,但确实需要将

.DATA
更改为 Promise,这可能会影响您可能未显示的其他代码。

© www.soinside.com 2019 - 2024. All rights reserved.