Implementarea primitivelor de sincronizare a evenimentelor în JavaScript/TypeScript folosind async/await/Promises


Am un proces asincron lung și complicat în TypeScript/JavaScript răspândit pe multe librării și funcții care, atunci când își termină în cele din urmă primirea și procesarea tuturor datelor, apelează o funcție processComplete() pentru a semnala că s-a terminat:

processComplete(); // Let the program know we're done processing

În acest moment, acea funcție arată cam așa:

let complete = false;

function processComplete() {
   complete = true;
}

Pentru a determina dacă procesul este complet, alt cod folosește fie time-out-uri, fie process.nextTick și verifică variabila complete din nou și din nou în bucle. Acest lucru este complicat și ineficient.

Aș prefera în schimb să las diverse funcții async să folosească pur și simplu await pentru a aștepta și a fi trezite când procesul este complet:

// This code will appear in many different places
await /* something regarding completion */;
console.log("We're done!");

Dacă aș programa în Windows în C, aș folosi o primitivă de sincronizare a evenimentelor și codul ar arăta cam așa:

Event complete;

void processComplete() {
   SetEvent(complete);
}

// Elsewhere, repeated in many different places
WaitForSingleObject(complete, INFINITE);
console.log("We're done!");

În JavaScript sau TypeScript, în loc să setezi o valoare booleană complete la true, ce anume ar putea face processComplete pentru a trezi orice număr de funcții care așteaptă folosind await? Cu alte cuvinte, cum pot implementa un primitiv de sincronizare a evenimentelor folosind await și async sau Promise-uri?

Răspuns #1

Acest model este destul de apropiat de codul tău:

const processComplete = args => new Promise(resolve => {
   // ...
   // In the middle of a callback for a async function, etc.: 
     resolve(); // instead of `complete = true;`
   // ...
}));

// elsewhere
await processComplete(args); 
console.log("We're done!");

Mai multe informații: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise.

Răspunsul #2

Depinde cu adevărat de ceea ce înțelegi prin „alt cod” în acest scenariu. Se pare că vrei să folosești o variație a modelului de delegare sau a modelului observator.

O abordare simplă este să profitați de faptul că JavaScript vă permite să stocați un array de funcții. Metoda dvs. processComplete() ar putea face ceva de genul acesta:

function processComplete(){
    arrayOfFunctions.forEach(fn => fn());
}

În altă parte, în celălalt cod al tău, ai putea crea funcții pentru ceea ce trebuie făcut atunci când procesul este complet și să adaugi acele funcții la arrayOfFunctions.

Dacă nu doriți ca aceste părți diferite ale codului să fie atât de strâns legate, ați putea să configurați o parte complet separată a codului dvs. care să funcționeze ca un centru de notificări. Apoi, codul dvs. celălalt ar spune centrului de notificări că dorește să fie notificat atunci când procesul este finalizat, iar metoda dvs. processComplete() ar spune pur și simplu centrului de notificări că procesul este finalizat.

O altă abordare este utilizarea promisiunilor.