简体   繁体   中英

Nestjs controller async problems

I am currently writing a finance-tracker app and have built a data import from a CSV file that checks if an entry is already in the database, if not adds a certain category to an entry and then saves it to the database.

After importing a CSV file, I want to output as a response to the Post Request: The number of imported entries as well as all entries for which no suitable category was found. Unfortunately, I fail because of the asynchronous approach of Nestjs. However, my response is output before the other functions are done. So that the first import is always “imports: 0” and “unsortedTransactions: [].

How can I wait in the controller for all functions to complete before returning a response? TransactionController:

@Controller('transactions')
export class TransactionController {
  constructor(
    private transactionService: TransactionService,
    private labelService: LabelService,
  ) {}

  @Post()
  @UseInterceptors(
    FileInterceptor('file', {
      storage: diskStorage({
        destination: './uploads/csv',
        filename: randomFilename,
      }),
    }),
  )
  public createTransactions(
    @Response() response,
    @UploadedFile() file,
    @Request() request,
  ) {
    let imports = 0;

    fs.createReadStream(path.resolve(file.path), {
      encoding: 'utf-8',
    })
      .pipe(csv.parse({ headers: false, delimiter: ';', skipRows: 1 }))
      .on('data', (data) => {
        const timestamp = new Date()
          .toISOString()
          .slice(0, 19)
          .replace('T', ' ');
        const amount = parseFloat(data[14].replace(',', '.'));
        const bankaccountId = request.body.bankaccount_id;
        const bookingDate = this.createDate(data[1]);
        const name = data[11].replace(/\s\s+/g, ' ');
        const usage = data[4].replace(/\s\s+/g, ' ');

        this.transactionService
          .checkDuplicate(amount, name, usage, bookingDate)
          .then((entry) => {
            if (entry.length === 0) {
              this.sortLabel(data).then((label_id) => {
                const newTransaction = {
                  amount,
                  name,
                  usage,
                  label_id,
                  bankaccount_id: bankaccountId,
                  booking_date: bookingDate,
                  created_at: timestamp,
                  updated_at: timestamp,
                };

                this.transactionService.create(newTransaction);
                imports++;
              });
            }
          });
      });

    const unsortedTransactions = this.transactionService.getUnsorted();
    return response.send({ unsortedTransactions, imports });
  }

  private async sortLabel(transaction: Transaction): Promise<any> {
    let label_id = 1;
    const labels = await this.labelService.getAll();
    const name = transaction[11].replace(/\s\s+/g, ' ').toLowerCase();
    const usage = transaction[4].replace(/\s\s+/g, ' ').toLowerCase();

    labels.forEach((label) => {
      if (label.keywords != null) {
        const keywords = label.keywords.split(',');

        keywords.forEach((keyword) => {
          if (
            name.includes(keyword.toLowerCase()) ||
            usage.includes(keyword.toLowerCase())
          ) {
            label_id = label.id;
          }
        });
      }
    });

    return await label_id;
  }

  private createDate(date: string): string {
    const dateArray = date.split('.');

    return `20${dateArray[2]}-${dateArray[1]}-${dateArray[0]}`;
  }
}

Transaction Service:

export class TransactionService {
  constructor(
    @InjectRepository(Transaction)
    private readonly transactionRepository: Repository<Transaction>,
  ) {}

  public create(transaction): Promise<Transaction> {
    return this.transactionRepository.save(transaction);
  }

  public getAll(): Promise<Transaction[]> {
    return this.transactionRepository.find({
      relations: ['label_id', 'bankaccount_id'],
    });
  }

  public getUnsorted(): Promise<Transaction[]> {
    return this.transactionRepository.find({
      where: {
        label_id: 1,
      },
    });
  }

  public checkDuplicate(
    amount: number,
    name: string,
    usage: string,
    booking_date: string,
  ): Promise<Transaction[]> {
    return this.transactionRepository.find({
      where: {
        amount,
        name,
        usage,
        booking_date,
      },
    });
  }
}

You need to take advantage of async / await functionality in js. You are returning promises from your service so this is easy.

Make the following changes

Add the async keyword to a function. (This allows you to await other functions inside)

  public createTransactions(
    @Response() response,
    @UploadedFile() file,
    @Request() request,
  ) async {....
  fs.createReadStream(path.resolve(file.path), {
      encoding: 'utf-8',
    })
      .pipe(csv.parse({ headers: false, delimiter: ';', skipRows: 1 }))
      .on('data', async (data) => {

        //... removed for brevity

        const checkDuplicateRes = await this.transactionService.checkDuplicate(
          amount,
          name,
          usage,
          bookingDate,
        );

        if (checkDuplicateRes.length === 0) {
          const labelId = await this.sortLabel(data);
          const newTransaction = {
            amount,
            name,
            usage,
            label_id: labelId,
            bankaccount_id: bankaccountId,
            booking_date: bookingDate,
            created_at: timestamp,
            updated_at: timestamp,
          };

          await this.transactionService.create(newTransaction);
          imports++;
        }
        const unsortedTransactions = await this.transactionService.getUnsorted();
        return response.send({ unsortedTransactions, imports });
      });

  }

Here we are basically replacing the .then syntax with the above. If you aren't familiar, i'd recommend reading up about it as it is much easier to follow than chaining .then() s

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM