WorkerClient

درخواست و پاسخ روی یک Web Worker. کارگرِ خام تنها «فرستادن پیام» و «گرفتن پیام» را می‌شناسد: دو کار را هم‌زمان بفرستید، دو پیام برمی‌گردد و راهی نیست بدانید کدام از آنِ کدام است. WorkerClient بر هر درخواست شناسه‌ای می‌کوبد و هر پاسخ را به Promiseِ خودش می‌رساند.

API

new WorkerClient(options)

پارامتر توضیح نوع پیش‌فرض
create اینکه کارگر چگونه ساخته شود () => Worker الزامی
isProgress آیا این پیام پیشرفت است؟ (درخواست را تعیین تکلیف نمی‌کند) (res) => boolean res.type === 'progress'
getProgress محتوای پیشرفت را بیرون می‌کشد (res) => Progress res.progress
isError آیا این پیام خطاست؟ (res) => boolean res.type === 'error'
getErrorMessage متن خطا (res) => string res.message
timeout مهلت هر درخواست (میلی‌ثانیه)؛ تنها همان درخواست را رد می‌کند number ندارد
عضو توضیح
send(request, onProgress?, transfer?) یک درخواست می‌فرستد و چشم‌به‌راه پاسخش می‌ماند
dispose() کارگر را تمام می‌کند و هر چه در جریان است رد می‌کند
active اینکه کارگر ساخته شده است یا نه
pendingCount شمار درخواست‌های در جریان

serveWorker(handler, options?) — سمت کارگر

همتایی که درون کارگر می‌دود. از هر درخواست operationId را می‌خواند، چشم‌به‌راه رسیدگی‌کنندهٔ شما می‌ماند و پاسخ را با همان شناسه پس می‌فرستد.

پارامتر توضیح نوع
handler (request, { progress }) => Response | Promise<Response> Function
options.scope جایی که گوش می‌دهد. پیش‌فرض self است؛ برای یک درگاه یا آزمون آن را عوض کنید object
options.resultType مقدار type پاسخ، وقتی رسیدگی‌کننده چیزی جز شیء برگرداند. پیش‌فرض 'result' string

تابعی به نام stop برمی‌گرداند که شنونده را برمی‌دارد.

نمونه

import { WorkerClient } from 'ranuts';

const client = new WorkerClient({
  create: () => new Worker(new URL('./nlp.worker.ts', import.meta.url), { type: 'module' }),
});

await client.send({ type: 'load', modelId }, (p) => renderProgress(p.progress));
const { scores } = await client.send({ type: 'classify', lines });
client.dispose();

و سمت کارگر:

// nlp.worker.ts
import { serveWorker } from 'ranuts';

serveWorker(async (request, { progress }) => {
  if (request.type === 'load') {
    const device = await loadModel(request.modelId, (p) => progress(p));
    return { type: 'loaded', device };
  }
  return { type: 'result', scores: await classify(request.lines) };
});

یادداشت‌ها

  1. کارگر تنبلانه ساخته می‌شود، در نخستین send: کار سنگین نباید هم‌زمان با بارگذاری صفحه آغاز شود.
  2. پیام‌های پیشرفت درخواست را تعیین تکلیف نمی‌کنند، پس یک درخواست می‌تواند به‌روزرسانی‌های بسیاری بفرستد و باز هم در پایان یک بار برآورده شود.
  3. فروپاشی کارگر همهٔ درخواست‌های در جریان را رد می‌کند. خطای گرفته‌نشده درون کارگر operationId ندارد، پس نمی‌توان آن را به یک درخواست نسبت داد.
  4. dispose() تمام می‌کند و رد می‌کند؛ send بعدی کارگر را از نو می‌سازد.
  5. پایان مهلت تنها همان درخواست را رد می‌کند و کارگر زنده می‌ماند.
  6. برای بافرهای بزرگ از transfer استفاده کنید تا به‌جای رونوشت ساختاری، مالکیت جابه‌جا شود.
  7. serveWorker خطاهای پرتاب‌شدهٔ همگام را هم می‌گیرد. پرتاب همگام درون onmessage به رسیدگی‌کنندهٔ خطای کارگر می‌گریزد و در آن مسیر هیچ operationId همراه نیست؛ آنگاه کارخواه ناچار است به‌جای همان یکی که واقعاً شکست، همهٔ درخواست‌های در جریان را ناکام کند.
  8. اینکه هر دو نیمه با هم عرضه می‌شوند عمدی است. دست‌ساز نوشتنِ سمت کارگر همان جایی است که بازتاب شناسه و پوششِ خطا میان پروژه‌ها از هم دور می‌افتند.