Liigu sisu juurde

AbortControlleri mustrid

AbortController ja AbortSignal on Phasis-is täielikud WHATWG teostused. Ka standardi koostamise abifunktsioonid (AbortSignal.any, AbortSignal.timeout) on kaasas. See leht käsitleb levinud mustreid ja seda, kuidas tühistamine levib üle PHP piiri.

Fetch-päringu tühistamine

JavaScript
const ctrl = new AbortController();
const p = fetch(url, { signal: ctrl.signal });
ctrl.abort();                     // p rejects with AbortError DOMException

Põhjus on vaikimisi AbortError, kuid võite määrata ka enda oma:

JavaScript
ctrl.abort(new Error('user cancelled'));

fetch lükatakse tagasi teie antud väärtusega.

Ajalimiit

JavaScript
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });

AbortSignal.timeout(ms) tagastab signaali, mis katkestab ise töö pärast aega ms. Taimer käivitub mootori sündmussilmuse kaudu, mis juhib ka setTimeout / queueMicrotask toiminguid (vt API: Sündmussilmus). Fetch-edastus kontrollib ülekande ajal signaali — vaikimisi CurlTransport puhul CURLOPT_PROGRESSFUNCTION kaudu —, seega katkestatakse päring kiiresti pärast tähtaja saabumist.

Vaikimisi kasutab tsükkel virtuaalaega: see viib kella järgmise ootel tähtajani, selle asemel et magada PHP, nii et fetch(url, { signal: AbortSignal.timeout(5000) }) kas lõpetab enne tähtaega või katkestatakse järgmisel tsüklitaktil — see ei jäta PHP kunagi ootama. Reaalajas toimimiseks peab host juhtima tickEventLoop() käitamist seinakella järgi.

Võidujooks: esimene katkestamine võidab

JavaScript
const user = new AbortController();
const signal = AbortSignal.any([user.signal, AbortSignal.timeout(10_000)]);
const res = await fetch(url, { signal });
// res rejects if EITHER the user clicks cancel OR 10s elapses

Leviku edasiandmine kihtide vahel

Mähi oma asünkroonne töö signaali arvestavasse tsüklisse:

JavaScript
async function fetchPaged(url, signal) {
  const out = [];
  for (let page = 1; ; page++) {
    if (signal.aborted) throw signal.reason;
    const r = await fetch(`${url}?page=${page}`, { signal });
    const data = await r.json();
    if (data.length === 0) break;
    out.push(...data);
  }
  return out;
}

Päring pärib signaali otse; käsitsi tehtav if (signal.aborted) katab päringutevahelised pausid, mil lehe tsükkel muidu edasi töötaks.

Katkestussündmuse kuulamine

JavaScript
ctrl.signal.addEventListener('abort', () => {
  releaseDatabaseLock();
  cancelDownstreamWork();
});

signal.reason määratakse enne kuulajate käivitamist.

Signaal kohandatud päringu transpordis

Kui vahetate transpordi välja setFetchTransport() kaudu, antakse signaal teie väljakutsutavale funktsioonile kui JsObject. Arvestage sellega, kontrollides seda perioodiliselt:

PHP
$engine->setFetchTransport(function (array $req, $signal) {
    $ch = curl_init($req['url']);
    // ...
    if ($signal !== null) {
        curl_setopt($ch, CURLOPT_PROGRESSFUNCTION,
            fn () => $signal->get('aborted')->value ? 1 : 0);
        curl_setopt($ch, CURLOPT_NOPROGRESS, false);
    }
    $body = curl_exec($ch);
    if (curl_errno($ch) === CURLE_ABORTED_BY_CALLBACK) {
        throw new \Phasis\BuiltIn\Fetch\TransportException(
            'aborted', 'aborted',
        );
    }
    // ...
});

kind: 'aborted' abil TransportException viskamine paneb JS-poole fetch-i tagasi lükkama veaga AbortError DOMException — sama juhtub siis, kui spetsifikatsioonikohane katkestustsükkel oleks käivitunud.

Natiivset katkestamist toetavate taustateenuste puhul (Guzzle on_progress, Symfony ResponseInterface::cancel) kasutage sama perioodilise kontrolli ja vea viskamise mustrit.

Mustreid, mis veel ei tööta

  • signal.addEventListener('abort', () => fetch(cleanupUrl)) — kuulaja käivitatakse sünkroonselt; puhastuspäring küll algab, kuid katkestuse töötlemise ajal ei jõua lõpule. Ajastage see ümbritseva await kaudu.

setTimeout(() => ctrl.abort(), N) töötab (taimerite perekond on kaasas ja WPT-ga kaetud), kuid AbortSignal.timeout(N) on lühem ja väljendab kavatsust paremini, kui soovite vaid tähtaja saabudes katkestava signaali.

Vt ka