Usullar9 min read

Task scheduling

Task scheduling sizga istalgan kodni (metod/funksiyalarni) belgilangan sana/vaqtda, qayta takrorlanuvchi intervalda yoki ko'rsatilgan intervaldan so'ng bir marta bajarishni rejalas

Task scheduling sizga istalgan kodni (metod/funksiyalarni) belgilangan sana/vaqtda, qayta takrorlanuvchi intervalda yoki ko'rsatilgan intervaldan so'ng bir marta bajarishni rejalashtirish imkonini beradi. Linux olamida bu odatda OS darajasida cron kabi paketlar bilan bajariladi. Node.js ilovalari uchun cron ga o'xshash funksionallikni emulyatsiya qiladigan bir nechta paketlar mavjud. Nest @nestjs/schedule paketini taqdim etadi, u mashhur Node.js cron paketi bilan integratsiya qiladi. Ushbu bobda aynan shu paketni ko'rib chiqamiz.

O'rnatish

Uni ishlatishni boshlash uchun avvalo kerakli bog'liqliklarni o'rnatamiz.

Terminal
1$ npm install --save @nestjs/schedule

Job schedulingni yoqish uchun ScheduleModule ni root AppModule ga import qiling va quyida ko'rsatilgandek forRoot() statik metodini ishga tushiring:

TypeScript
app.module
1import { Module } from '@nestjs/common';
2import { ScheduleModule } from '@nestjs/schedule';
3
4@Module({
5  imports: [
6    ScheduleModule.forRoot()
7  ],
8})
9export class AppModule {}

.forRoot() chaqiruvi scheduler'ni inicializatsiya qiladi va ilovangizda mavjud bo'lgan deklarativ cron joblar, timeoutlar va intervallarni ro'yxatdan o'tkazadi. Ro'yxatdan o'tkazish onApplicationBootstrap lifecycle hook sodir bo'lganda amalga oshadi, bu esa barcha modullar yuklanganini va rejalashtirilgan ishlarni e'lon qilganini ta'minlaydi.

Deklarativ cron job'lar

Cron job istalgan funksiyani (metod chaqiruvini) avtomatik bajarishga rejalashtiradi. Cron job'lar quyidagicha ishlashi mumkin:

  • Bir marta, belgilangan sana/vaqtda.
  • Qayta takrorlanuvchi asosda; takrorlanuvchi ishlar belgilangan interval ichida ma'lum bir vaqtda ishlashi mumkin (masalan, soatiga bir marta, haftasiga bir marta, har 5 daqiqada bir marta)

Cron job'ni @Cron() dekoratorini bajariladigan kod joylashgan metoddan oldin qo'yish orqali e'lon qiling, quyidagicha:

TypeScript
1import { Injectable, Logger } from '@nestjs/common';
2import { Cron } from '@nestjs/schedule';
3
4@Injectable()
5export class TasksService {
6  private readonly logger = new Logger(TasksService.name);
7
8  @Cron('45 * * * * *')
9  handleCron() {
10    this.logger.debug('Called when the current second is 45');
11  }
12}

Ushbu misolda handleCron() metodi joriy soniya 45 bo'lganda chaqiriladi. Boshqacha aytganda, metod har daqiqada bir marta, 45-soniyada ishga tushadi.

@Cron() dekoratori quyidagi standart cron patternlarni qo'llab-quvvatlaydi:

  • Asterisk (masalan, *)
  • Ranges (masalan, 1-3,5)
  • Steps (masalan, */2)

Yuqoridagi misolda dekoratorga 45 * * * * * ni uzatdik. Quyidagi kalit cron pattern satridagi har bir pozitsiya qanday talqin qilinishini ko'rsatadi:

JavaScript
1* * * * * *
2| | | | | |
3| | | | | day of week
4| | | | months
5| | | day of month
6| | hours
7| minutes
8seconds (optional)

Ba'zi cron pattern namunalari:

* * * * * *har soniyada
45 * * * * *har daqiqada, 45-soniyada
0 10 * * * *har soatda, 10-daqiqa boshida
0 */30 9-17 * * *09:00 dan 17:00 gacha har 30 daqiqada
0 30 11 * * 1-5Monday to Friday at 11:30am

@nestjs/schedule paketi ko'p ishlatiladigan cron patternlar uchun qulay enum taqdim etadi. Bu enumdan quyidagicha foydalanishingiz mumkin:

TypeScript
1import { Injectable, Logger } from '@nestjs/common';
2import { Cron, CronExpression } from '@nestjs/schedule';
3
4@Injectable()
5export class TasksService {
6  private readonly logger = new Logger(TasksService.name);
7
8  @Cron(CronExpression.EVERY_30_SECONDS)
9  handleCron() {
10    this.logger.debug('Called every 30 seconds');
11  }
12}

Ushbu misolda handleCron() metodi har 30 soniyada chaqiriladi. Agar istisno yuzaga kelsa, u konsolga loglanadi, chunki @Cron() bilan belgilangan har bir metod avtomatik ravishda try-catch blokiga o'raladi.

Muqobil ravishda, @Cron() dekoratoriga JavaScript Date obyektini uzatishingiz mumkin. Bunda job ko'rsatilgan sanada aniq bir marta ishlaydi.

Hint

Joriy sanaga nisbatan job rejalashtirish uchun JavaScript date arithmetic'dan foydalaning. Masalan, @Cron(new Date(Date.now() + 10 * 1000)) ilova ishga tushganidan 10 soniya o'tib ishga tushadigan jobni rejalashtiradi.

Shuningdek, @Cron() dekoratoriga ikkinchi parametr sifatida qo'shimcha opsiyalarni uzatishingiz mumkin.

name E'lon qilingandan so'ng cron jobga kirish va uni boshqarish uchun foydali.
timeZone Bajarilish uchun vaqt zonasini ko'rsating. Bu haqiqiy vaqtni sizning time zone'ingizga nisbatan o'zgartiradi. Agar time zone noto'g'ri bo'lsa, xato tashlanadi. Mavjud barcha time zonalarni Moment Timezone saytida tekshirishingiz mumkin.
utcOffset Bu `timeZone` parametri o'rniga time zone offsetini ko'rsatish imkonini beradi.
waitForCompletion Agar true bo'lsa, joriy onTick callback tugamaguncha cron jobning qo'shimcha instansiyalari ishga tushmaydi. Joriy cron job ishlayotganda rejalashtirilgan yangi bajarilishlar butunlay o'tkazib yuboriladi.
disabled Bu job umuman bajariladimi-yo'qligini bildiradi.
TypeScript
1import { Injectable } from '@nestjs/common';
2import { Cron, CronExpression } from '@nestjs/schedule';
3
4@Injectable()
5export class NotificationService {
6  @Cron('* * 0 * * *', {
7    name: 'notifications',
8    timeZone: 'Europe/Paris',
9  })
10  triggerNotifications() {}
11}

E'lon qilingandan so'ng cron jobga kirish va uni boshqarish yoki cron patterni runtime paytida aniqlanadigan dinamik cron job yaratish uchun Dynamic API dan foydalanishingiz mumkin. API orqali deklarativ cron jobga kirish uchun dekoratorning ikkinchi argumenti sifatidagi ixtiyoriy opsiyalar obyektida name xossasini berib, jobga nom biriktirishingiz kerak.

Deklarativ interval'lar

Metod belgilangan (takrorlanuvchi) intervalda ishlashi kerakligini e'lon qilish uchun metod ta'rifidan oldin @Interval() dekoratorini qo'ying. Decoratorga millisekundlarda interval qiymatini uzating, quyidagicha:

TypeScript
1@Interval(10000)
2handleInterval() {
3  this.logger.debug('Called every 10 seconds');
4}
Hint

Bu mexanizm ichkarida JavaScript setInterval() funksiyasidan foydalanadi. Shuningdek, takrorlanuvchi ishlarni rejalashtirish uchun cron job'ni ham ishlatishingiz mumkin.

Agar deklarativ interval'ni e'lon qilgan sinfdan tashqarida Dynamic API orqali boshqarishni xohlasangiz, intervalni quyidagi konstruktsiya bilan nomga bog'lang:

TypeScript
1@Interval('notifications', 2500)
2handleInterval() {}

Agar istisno yuzaga kelsa, u konsolga loglanadi, chunki @Interval() bilan belgilangan har bir metod avtomatik ravishda try-catch blokiga o'raladi.

Dynamic API shuningdek runtime paytida interval xossalari aniqlanadigan dinamik interval'larni yaratish, hamda ularni ro'yxatlash va o'chirish imkonini beradi.

Deklarativ timeout'lar

Metod belgilangan timeoutdan keyin bir marta ishlashi kerakligini e'lon qilish uchun metod ta'rifidan oldin @Timeout() dekoratorini qo'ying. Decoratorga ilova ishga tushganidan boshlab millisekundlarda nisbiy vaqt ofsetini uzating, quyidagicha:

TypeScript
1@Timeout(5000)
2handleTimeout() {
3  this.logger.debug('Called once after 5 seconds');
4}
Hint

Bu mexanizm ichkarida JavaScript setTimeout() funksiyasidan foydalanadi.

Agar istisno yuzaga kelsa, u konsolga loglanadi, chunki @Timeout() bilan belgilangan har bir metod avtomatik ravishda try-catch blokiga o'raladi.

Agar deklarativ timeout'ni e'lon qilgan sinfdan tashqarida Dynamic API orqali boshqarishni xohlasangiz, timeoutni quyidagi konstruktsiya bilan nomga bog'lang:

TypeScript
1@Timeout('notifications', 2500)
2handleTimeout() {}

Dynamic API shuningdek runtime paytida timeout xossalari aniqlanadigan dinamik timeout'larni yaratish, hamda ularni ro'yxatlash va o'chirish imkonini beradi.

Dynamic schedule module API

@nestjs/schedule moduli deklarativ cron joblar, timeoutlar va intervallarni boshqarishga imkon beradigan dinamik API taqdim etadi. API shuningdek runtime paytida xossalari aniqlanadigan dinamik cron job'lar, timeout'lar va interval'larni yaratish va boshqarish imkonini beradi.

Dinamik cron job'lar

SchedulerRegistry API yordamida kodingizning istalgan joyidan nomi bo'yicha CronJob instansiyasiga havola oling. Avval SchedulerRegistry ni odatiy konstruktor in'eksiyasi orqali kiriting:

TypeScript
1constructor(private schedulerRegistry: SchedulerRegistry) {}
Hint

SchedulerRegistry ni @nestjs/schedule paketidan import qiling.

So'ng uni sinfda quyidagicha ishlating. Faraz qilaylik, quyidagi deklaratsiya bilan cron job yaratilgan:

TypeScript
1@Cron('* * 8 * * *', {
2  name: 'notifications',
3})
4triggerNotifications() {}

Ushbu jobga quyidagicha kiring:

TypeScript
1const job = this.schedulerRegistry.getCronJob('notifications');
2
3job.stop();
4console.log(job.lastDate());

getCronJob() metodi nomlangan cron jobni qaytaradi. Qaytgan CronJob obyektida quyidagi metodlar mavjud:

  • stop() - rejalashtirilgan jobni to'xtatadi.
  • start() - to'xtatilgan jobni qayta ishga tushiradi.
  • setTime(time: CronTime) - jobni to'xtatadi, yangi vaqt belgilaydi va keyin jobni qayta ishga tushiradi.
  • lastDate() - job oxirgi marta bajarilgan sana DateTime ko'rinishini qaytaradi.
  • nextDate() - job keyingi marta bajarilishi rejalashtirilgan sananing DateTime ko'rinishini qaytaradi.
  • nextDates(count: number) - job bajarilishini boshlatadigan keyingi sanalar uchun DateTime ko'rinishlaridan iborat massivni (o'lchami count) qaytaradi. count default holatda 0 bo'lib, bo'sh massiv qaytaradi.
Hint

DateTime obyektlarini JavaScript Date ekvivalentiga aylantirish uchun toJSDate() dan foydalaning.

SchedulerRegistry#addCronJob metodi yordamida yangi cron jobni dinamik yarating, quyidagicha:

TypeScript
1addCronJob(name: string, seconds: string) {
2  const job = new CronJob(`${seconds} * * * * *`, () => {
3    this.logger.warn(`time (${seconds}) for job ${name} to run!`);
4  });
5
6  this.schedulerRegistry.addCronJob(name, job);
7  job.start();
8
9  this.logger.warn(
10    `job ${name} added for each minute at ${seconds} seconds!`,
11  );
12}

Bu kodda biz cron paketidagi CronJob obyektidan foydalanib cron job yaratamiz. CronJob konstruktori birinchi argument sifatida cron patternni (@Cron() dekoratori kabi) va ikkinchi argument sifatida cron taymeri ishga tushganda bajariladigan callbackni oladi. SchedulerRegistry#addCronJob metodi ikki argument qabul qiladi: CronJob uchun nom va CronJob obyektining o'zi.

Warning

SchedulerRegistry ga kirishdan oldin uni in'eksiya qilishni unutmang. CronJob ni cron paketidan import qiling.

SchedulerRegistry#deleteCronJob metodi yordamida nomlangan cron jobni o'chiring, quyidagicha:

TypeScript
1deleteCron(name: string) {
2  this.schedulerRegistry.deleteCronJob(name);
3  this.logger.warn(`job ${name} deleted!`);
4}

SchedulerRegistry#getCronJobs metodi yordamida barcha cron joblarni ro'yxatlang, quyidagicha:

TypeScript
1getCrons() {
2  const jobs = this.schedulerRegistry.getCronJobs();
3  jobs.forEach((value, key, map) => {
4    let next;
5    try {
6      next = value.nextDate().toJSDate();
7    } catch (e) {
8      next = 'error: next fire date is in the past!';
9    }
10    this.logger.log(`job: ${key} -> next: ${next}`);
11  });
12}

getCronJobs() metodi map qaytaradi. Bu kodda biz map bo'ylab iteratsiya qilib, har bir CronJob ning nextDate() metodiga kirishga harakat qilamiz. CronJob APIda agar job allaqachon ishga tushgan bo'lsa va kelajakdagi ishga tushish sanasi bo'lmasa, u istisno tashlaydi.

Dinamik interval'lar

SchedulerRegistry#getInterval metodi yordamida intervalga havola oling. Yuqoridagi kabi, SchedulerRegistry ni odatiy konstruktor in'eksiyasi orqali kiriting:

TypeScript
1constructor(private schedulerRegistry: SchedulerRegistry) {}

Va quyidagicha foydalaning:

TypeScript
1const interval = this.schedulerRegistry.getInterval('notifications');
2clearInterval(interval);

SchedulerRegistry#addInterval metodi yordamida yangi intervalni dinamik yarating, quyidagicha:

TypeScript
1addInterval(name: string, milliseconds: number) {
2  const callback = () => {
3    this.logger.warn(`Interval ${name} executing at time (${milliseconds})!`);
4  };
5
6  const interval = setInterval(callback, milliseconds);
7  this.schedulerRegistry.addInterval(name, interval);
8}

Bu kodda biz standart JavaScript interval yaratamiz, so'ng uni SchedulerRegistry#addInterval metodiga uzatamiz. Ushbu metod ikki argument qabul qiladi: interval uchun nom va intervalning o'zi.

SchedulerRegistry#deleteInterval metodi yordamida nomlangan intervalni o'chiring, quyidagicha:

TypeScript
1deleteInterval(name: string) {
2  this.schedulerRegistry.deleteInterval(name);
3  this.logger.warn(`Interval ${name} deleted!`);
4}

SchedulerRegistry#getIntervals metodi yordamida barcha interval'larni ro'yxatlang, quyidagicha:

TypeScript
1getIntervals() {
2  const intervals = this.schedulerRegistry.getIntervals();
3  intervals.forEach(key => this.logger.log(`Interval: ${key}`));
4}

Dinamik timeout'lar

SchedulerRegistry#getTimeout metodi yordamida timeoutga havola oling. Yuqoridagi kabi, SchedulerRegistry ni odatiy konstruktor in'eksiyasi orqali kiriting:

TypeScript
1constructor(private readonly schedulerRegistry: SchedulerRegistry) {}

Va quyidagicha foydalaning:

TypeScript
1const timeout = this.schedulerRegistry.getTimeout('notifications');
2clearTimeout(timeout);

SchedulerRegistry#addTimeout metodi yordamida yangi timeoutni dinamik yarating, quyidagicha:

TypeScript
1addTimeout(name: string, milliseconds: number) {
2  const callback = () => {
3    this.logger.warn(`Timeout ${name} executing after (${milliseconds})!`);
4  };
5
6  const timeout = setTimeout(callback, milliseconds);
7  this.schedulerRegistry.addTimeout(name, timeout);
8}

Bu kodda biz standart JavaScript timeout yaratamiz, so'ng uni SchedulerRegistry#addTimeout metodiga uzatamiz. Ushbu metod ikki argument qabul qiladi: timeout uchun nom va timeoutning o'zi.

SchedulerRegistry#deleteTimeout metodi yordamida nomlangan timeoutni o'chiring, quyidagicha:

TypeScript
1deleteTimeout(name: string) {
2  this.schedulerRegistry.deleteTimeout(name);
3  this.logger.warn(`Timeout ${name} deleted!`);
4}

SchedulerRegistry#getTimeouts metodi yordamida barcha timeout'larni ro'yxatlang, quyidagicha:

TypeScript
1getTimeouts() {
2  const timeouts = this.schedulerRegistry.getTimeouts();
3  timeouts.forEach(key => this.logger.log(`Timeout: ${key}`));
4}

Misol

Ishlaydigan misol bu yerda mavjud.