Utilitalar
debounce, throttle, sleep, groupBy, uniqueBy va clamp — umumiy yordamchi funksiyalar.
Har loyihada qayta yoziladigan kichik funksiyalar. Bog'liqliksiz, React'siz va to'liq tiplangan.
import { debounce, throttle, sleep, groupBy, uniqueBy, clamp } from '@akhatmirzo/core';Vaqt bo'yicha cheklash
Sichqonchani maydon ustida yurgizing va hisoblagichlarni solishtiring: throttle harakat
davomida har 400 ms da bir marta ishlaydi, debounce esa faqat to'xtaganingizdan keyin bir marta.
- raw
- 0
- throttled
- 0
- debounced
- 0
'use client';
import { debounce, throttle } from '@akhatmirzo/core';
import { Button } from '@akhatmirzo/ui';
import { useEffect, useMemo, useState } from 'react';
type Counts = { raw: number; throttled: number; debounced: number };
const ZERO: Counts = { raw: 0, throttled: 0, debounced: 0 };
const inc = (key: keyof Counts) => (c: Counts) => ({ ...c, [key]: c[key] + 1 });
export default function CoreDebounceThrottle() {
const [counts, setCounts] = useState(ZERO);
const onDebounced = useMemo(() => debounce(() => setCounts(inc('debounced')), 400), []);
const onThrottled = useMemo(() => throttle(() => setCounts(inc('throttled')), 400), []);
useEffect(
() => () => {
onDebounced.cancel();
onThrottled.cancel();
},
[onDebounced, onThrottled],
);
return (
<div className="flex w-full max-w-md flex-col gap-3">
<div
onPointerMove={() => {
setCounts(inc('raw'));
onDebounced();
onThrottled();
}}
className="flex h-32 touch-none items-center justify-center rounded-xl border border-dashed border-border-strong bg-surface-muted text-sm text-text-muted"
>
Sichqoncha yoki barmoqni shu yerda yurgizing
</div>
<dl className="grid grid-cols-3 gap-2 text-center text-sm">
{(['raw', 'throttled', 'debounced'] as const).map((key) => (
<div key={key} className="rounded-lg border border-border bg-surface p-2">
<dt className="font-mono text-xs text-text-muted">{key}</dt>
<dd className="text-xl font-semibold text-text tabular-nums">{counts[key]}</dd>
</div>
))}
</dl>
<Button size="sm" variant="ghost" onClick={() => setCounts(ZERO)}>
Nolga qaytarish
</Button>
</div>
);
}
debounce
function debounce<A extends unknown[]>(fn: (...args: A) => unknown, ms: number): Debounced<A>;
interface Debounced<A> {
(...args: A): void;
cancel(): void; // kutilayotgan chaqiruvni bekor qiladi
flush(): void; // kutilayotgan chaqiruvni darhol bajaradi
pending(): boolean; // kutilayotgan chaqiruv bormi
}Oxirgi chaqiruvdan ms millisekund o'tgach fn ni oxirgi argumentlar bilan bir marta
chaqiradi. Qidiruv maydoni, avtomatik saqlash, oyna o'lchami o'zgarishi uchun.
const search = debounce((query: string) => api.get('/search', { query: { q: query } }), 300);
search('t');
search('to');
search('tosh'); // 300 ms dan keyin faqat bitta so'rov: q=tosh
search.pending(); // true
search.flush(); // darhol yuborish (masalan, Enter bosilganda)
search.cancel(); // bekor qilish (masalan, komponent yopilganda)React komponentida
Komponent ichida har render'da yangi debounce yaratilmasligi kerak.
useDebouncedCallback buni o'zi hal qiladi va unmount'da
bekor qiladi.
throttle
function throttle<A extends unknown[]>(fn: (...args: A) => unknown, ms: number): Throttled<A>;
interface Throttled<A> {
(...args: A): void;
cancel(): void; // kutilayotgan (oxirgi) chaqiruvni bekor qiladi
}fn ni ko'pi bilan ms da bir marta chaqiradi: birinchi chaqiruv darhol, oraliqda
kelganlarning oxirgisi — oraliq tugaganda. Scroll, sichqoncha harakati, tez-tez keladigan
hodisalar uchun.
const onScroll = throttle(() => saveScrollPosition(window.scrollY), 100);
window.addEventListener('scroll', onScroll);
const log = throttle((n: number) => console.log(n), 50);
log(1); // darhol: 1
log(2);
log(3); // 50 ms dan keyin: 3 (2 tashlab yuboriladi)sleep
function sleep(ms: number, signal?: AbortSignal): Promise<void>;ms millisekund kutadi. signal bekor qilinsa, promise signal.reason (yoki AbortError)
bilan reject bo'ladi — kutish osilib qolmaydi.
await sleep(500);
// 3 marta urinish, oralarda 1 va 2 soniya kutib
async function getStatus() {
for (let attempt = 0; ; attempt++) {
try {
return await api.get('/status');
} catch (error) {
if (attempt === 2) throw error;
await sleep(1000 * 2 ** attempt);
}
}
}
const controller = new AbortController();
sleep(10_000, controller.signal).catch((e) => e.name); // 'AbortError'
controller.abort();Massivlar
groupBy
function groupBy<T, K extends PropertyKey>(
items: readonly T[],
keyFn: (item: T, index: number) => K,
): Record<K, T[]>;Elementlarni kalit bo'yicha guruhlaydi, tartib saqlanadi. Natija prototipsiz obyekt — 'constructor'
yoki 'toString' kabi kalitlar ham xavfsiz.
const users = [
{ name: 'Ali', role: 'admin' },
{ name: 'Vali', role: 'user' },
{ name: 'Sardor', role: 'admin' },
];
groupBy(users, (u) => u.role);
// {
// admin: [{ name: 'Ali', … }, { name: 'Sardor', … }],
// user: [{ name: 'Vali', … }],
// }uniqueBy
function uniqueBy<T>(items: readonly T[], keyFn: (item: T, index: number) => unknown): T[];Kalit bo'yicha takrorlarni olib tashlaydi — har bir kalitning birinchi elementi qoladi.
uniqueBy(
[
{ id: 1, name: 'Toshkent' },
{ id: 2, name: 'Samarqand' },
{ id: 1, name: 'Toshkent (eski)' },
],
(city) => city.id,
);
// [{ id: 1, name: 'Toshkent' }, { id: 2, name: 'Samarqand' }]Sonlar
clamp
function clamp(value: number, min: number, max: number): number;Qiymatni [min, max] oralig'iga cheklaydi.
clamp(150, 0, 100); // 100
clamp(-5, 0, 100); // 0
clamp(42, 0, 100); // 42