A Promise objektum egy aszinkron művelet későbbi befejeződését (vagy meghiúsulását) és annak eredményül kapott értékét képviseli.
Ez sokaknak nehéz téma, különösen, ha olyan nyelven programozol, amely teljesen szinkron. Ha úgy érzed, elárasztanak az információk, vagy szeretnél többet megtudni a konkurenciáról és a párhuzamosságról, nézd meg (a go.dev-en) vagy nézd meg közvetlenül a Vimeón, és olvasd el a diákat a zseniális „Concurrency is not parallelism” előadáshoz.
A Promise-nak három állapota van:
Amikor létrejön, egy promise függőben van. A jövőben valamikor teljesülhet vagy elutasításra kerülhet. Ha egy promise egyszer már teljesült vagy elutasításra került, soha többé nem teljesülhet újra, és nem is utasítható el, az állapota sem változhat meg.
Más szóval:
Egy promise többféleképpen is teljesíthető:
// Creates a promise that is immediately resolved
Promise.resolve(value);
// Creates a promise that is immediately resolved
new Promise((resolve) => {
resolve(value);
});
// Chaining a promise leads to a resolved promise
somePromise.then(() => {
// ...
return value;
});
A fenti példákban a value bármi lehet, akár egy hiba, undefined, null vagy egy másik promise is.
Általában olyan értékkel akarsz teljesíteni, amely nem hiba.
Egy promise többféleképpen is elutasítható:
// Creates a promise that is immediately rejected
Promise.reject(reason)
// Creates a promise that is immediately rejected
new Promise((_, reject) {
reject(reason)
})
// Chaining a promise with an error leads to a rejected promise
somePromise.then(() => {
// ...
throw reason
})
A fenti példákban a reason bármi lehet, akár egy hiba, undefined vagy null is.
Általában egy hibával akarsz elutasítani.
Egy promise egy későbbi művelettel folytatható, amint teljesül vagy elutasításra kerül.
promise.then() akkor hívódik meg, amikor a promise teljesülpromise.catch() akkor hívódik meg, amikor a promise elutasításra kerülpromise.finally() akkor hívódik meg, amikor a promise akár teljesül, akár elutasításra kerülMinden promise „thenable”.
Ez azt jelenti, hogy elérhető egy then nevű függvény, amely akkor fut le, amikor az eredeti promise teljesül.
A promise.then(onResolved) esetén az onResolved visszahívás azt az értéket kapja, amellyel az eredeti promise teljesült.
Ez mindig egy új, „láncolt” promise-szal tér vissza.
Ha egy value-t adsz vissza a then-ből, az teljesíti a „láncolt” promise-t.
Ha egy reason-t dobsz a then-ben, az elutasítja a „láncolt” promise-t.
const promise1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('Success!');
}, 1000);
});
const promise2 = promise1.then(function (value) {
console.log(value);
// expected output: "Success!"
return true;
});
Ez körülbelül 1000 ms után kiírja, hogy "Success!".
A promise1 állapota és értéke resolved és "Success!" lesz.
A promise2 állapota és értéke resolved és true lesz.
Van egy második argumentum is, amely akkor fut le, amikor az eredeti promise elutasításra kerül.
A promise.then(onResolved, onRejected) esetén az onResolved visszahívás azt az értéket kapja, amellyel az eredeti promise teljesült, vagy az onRejected visszahívás azt az indokot kapja, amellyel a promise elutasításra került.
const promise1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('Success!');
}, 1000);
if (Math.random() < 0.5) {
reject('Nope!');
}
});
function log(value) {
console.log(value);
return true;
}
function shout(reason) {
console.error(reason.toUpperCase());
return false;
}
const promise2 = promise1.then(log, shout);
"Success!".
promise1 állapota és értéke resolved és "Success!" lesz.promise2 állapota és értéke resolved és true lesz."NOPE!".
promise1 állapota és értéke rejected és Nope! lesz.promise2 állapota és értéke resolved és false lesz.Fontos megérteni, hogy az életciklus szabályai miatt, amikor a promise elutasításra kerül (reject), a körülbelül 1000 ms-mal később érkező resolve csendben figyelmen kívül marad, mert a belső állapot nem változhat meg, miután egyszer elutasításra került vagy teljesült.
Fontos megérteni azt is, hogy ha egy promise-ból értéket adsz vissza, az teljesíti azt, ha pedig értéket dobsz, az elutasítja.
Amikor a promise1 teljesül, és van egy láncolt onResolved: then(onResolved), akkor az a folytatás egy új promise, amely teljesülhet vagy elutasításra kerülhet.
Amikor a promise1 elutasításra kerül, de van egy láncolt onRejected: then(, onRejected), akkor az a folytatás egy új promise, amely teljesülhet vagy elutasításra kerülhet.
Néha szeretnéd elkapni a hibákat, és csak akkor folytatni, amikor az eredeti promise elutasításra kerül (reject).
A promise.catch(onCatch) esetén az onCatch visszahívás azt az indokot kapja, amellyel az eredeti promise elutasításra került.
Ez mindig egy új, „láncolt” promise-szal tér vissza.
Ha egy value-t adsz vissza a catch-ből, az teljesíti a „láncolt” promise-t.
Ha egy reason-t dobsz a catch-ben, az elutasítja a „láncolt” promise-t.
const promise1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('Success!');
}, 1000);
if (Math.random() < 0.5) {
reject('Nope!');
}
});
function log(value) {
console.log(value);
return 'done';
}
function recover(reason) {
console.error(reason.toUpperCase());
return 42;
}
const promise2 = promise1.catch(recover).then(log);
Az esetek nagyjából felében ez körülbelül 1000 ms után kiírja, hogy "Success!".
A másik felében ez azonnal kiírja a 42-t.
promise1 teljesül, a catch kimarad, eljut a then-ig, és az kiírja az értéket.
promise1 állapota és értéke resolved és "Success!" lesz.promise2 állapota és értéke resolved és "done" lesz;promise1 elutasításra kerül, a catch lefut, amely visszaad egy értéket, így a lánc mostantól resolved, eljut a then-ig, és az kiírja az értéket.
promise1 állapota és értéke rejected és "Nope!" lesz.promise2 állapota és értéke resolved és "done" lesz;Néha szeretnél kódot futtatni, miután egy promise lezárul, függetlenül attól, hogy teljesül-e vagy elutasításra kerül.
A promise.finally(onSettled) esetén az onSettled visszahívás semmit sem kap.
Ez mindig egy új, „láncolt” promise-szal tér vissza.
Ha egy value-t adsz vissza a finally-ből, az átmásolja az eredeti promise állapotát és értékét, figyelmen kívül hagyva a value-t.
Ha egy reason-t dobsz a finally-ben, az elutasítja a „láncolt” promise-t, felülírva az eredeti promise bármilyen állapotát, értékét vagy indokát.
Több metódus együtt:
const myPromise = new Promise(function (resolve, reject) {
const sampleData = [2, 4, 6, 8];
const randomNumber = Math.round(Math.random() * 5);
if (sampleData[randomNumber]) {
resolve(sampleData[randomNumber]);
} else {
reject('Sampling did not result in a sample');
}
});
const finalPromise = myPromise
.then(function (sampled) {
// If the random number was 0, 1, 2, or 3, this will be
// reached and the number 2, 4, 6, or 8 will be logged.
console.log(`Sampled data: ${sampled}`);
return 'yay';
})
.catch(function (reason) {
// If the random number was 4 or 5, this will be reached and
// reason will be "An error occurred". The entire chain will
// then reject with an Error with the reason as message.
throw new Error(reason);
})
.finally(function () {
// This will always log after either the sampled data is
// logged or the error is raised.
console.log('Promise completed');
});
randomNumber 0-3:
myPromise a 2, 4, 6, or 8 értékkel teljesülfinalPromise a 'yay' értékkel teljesülSampled data: ...Promise completedrandomNumber 4-5:
myPromise a 'Sampling did not result in a sample' indokkal utasításra kerülfinalPromise a Error('Sampling did not result in a sample') indokkal utasításra kerülPromise completed"uncaught rejected promise: Error('Sampling did not result in a sample')" naplóbejegyzést eredményezAhogy fentebb látható, a reject stringgel is működik, és egy promise egy Error-ral is elutasíthat.
Ha a promise-ok láncolása vagy az általános használat nem világos, az MDN oktatóanyaga jó forrás, amit érdemes átolvasni.
Ebben a feladatban egy TranslationService-t készítesz, amely alapvető fordítási szolgáltatásokat nyújt az ingyenes tagoknak, és fejlett fordítást a prémium tagoknak, minőségbiztosítással.
Az API
Találtál egy világűrbeli fordítási API-t, amely ésszerű időn belül teljesít bármilyen fordítási request-et.
Ezt szeretnéd kihasználni.
Az űrbéli fordítók rendkívül szeszélyesek, és utálják a felesleges ismétlődést, ezért API storage műholdakat is biztosítanak, ahonnan fetch-csel lekérheted a korábbi fordításokat anélkül, hogy zavarnád őket.
Fordítás lekérése
Az api.fetch(text) lekéri a text fordítását az API storage-ból, és egy promise-t ad vissza, amely két értéket biztosít:
translation: maga a fordításquality: a minőség számmal kifejezveHa egy fordítás nem található az API storage-ban, az API egy NotAvailable hibát dob.
A fordításokat az api.request metódussal lehet hozzáadni.
Ha a „text” nem fordítható le, az API egy Untranslatable hibát dob.
api.fetch('jIyaj');
// => Promise({ resolved: 'I understand' })
Fordítás kérése
Egyes fordítások biztosan léteznek, de még nem kerültek be az API storage-ba. Ez a különbség a NotAvailable (nincs a tárhelyen, de kérhető) és az Untranslatable (nem fordítható le) között.
Az api.request(text, callback) azt kéri, hogy készüljön el a text fordítása, és kerüljön be az API storage-ba.
Amikor elkészül, meghívódik a callback függvény.
callback az undefined értéket kapja: ez azt jelzi, hogy a fordítás sikeres volt, és az api.fetch metódussal érhető el.callback egy error-t kap: ez azt jelzi, hogy valami hiba történt.
Az űrbéli API instabil, ami azt jelenti, hogy az API gyakran hibázik.
Ha ez megtörténik, nyugodtan meghívhatod újra az api.request-et.api.request('majQa’', callback);
// => undefined
//
// later: the passed callback is called with undefined
// because it was successful.
⚠ Figyelem! ⚠
Az API úgy fejti ki a varázsát, hogy minden beérkező request hatására odateleportálja a különböző fordítókat.
Ez nagyon költséges művelet, ezért nem szabad meghívni, amikor egy fordítás elérhető.
Sajnos nem mindenki olvassa el a kézikönyvet, ezért létezik egy rendszer, amely kizárja a renitens felhasználókat.
Ha az api.request-et egy elérhető text-re hívják meg, az API egy AbusiveClientError-t dob erre a hívásra, és minden utána következőre is.
Ügyelj rá, hogy soha ne kérj fordítást, ha valamit már lefordítottak.
Az ingyenes szolgáltatás csak azokat a fordításokat nyújtja, amelyek éppen benne vannak az API storage-ban.
Készíts egy free(text) metódust, amely az ingyenes tagoknak az API storage-ban már létező fordításokat nyújtja.
Hagyd figyelmen kívül a minőséget, és add tovább az API által dobott hibákat.
api.fetch metódust használja (az api.fetch egy promise-t ad vissza)service.free('jIyaj');
// => Promise<...> resolves "I understand."
service.free("jIyajbe'");
// => Promise<...> rejects Error("Not yet translated")
Készíts egy batch([text, text, ...]) metódust az ingyenes tagoknak, amely az ingyenes szolgáltatással lefordít egy szövegtömböt, és vagy az összes fordítást adja vissza, vagy egyetlen hibát.
BatchIsEmpty hibával utasít el, ha nincs megadva szövegservice.batch(['jIyaj', "majQa'"]);
// => Promise<...> resolves ["I understand.", "Well done!"]
service.batch(['jIyaj', "jIyajbe'"]);
// => Promise<...> rejects new Error("Not yet translated")
service.batch([]);
// => Promise<...> rejects BatchIsEmpty()
Készíts egy request(text) metódust a prémium felhasználóknak, amely azt kéri, hogy egy fordítás kerüljön be az API storage-ba.
A kérésnek hiba esetén automatikusan újra kell próbálkoznia.
Ugyanarra a kérésre legfeljebb 3 hívást hajthat végre (ne bosszantsd fel az űrbéli fordítókat!!!).
api.request nem ad vissza hibát, undefined értékkel teljesülapi.request hibát ad vissza, próbálkozz újra legfeljebb kétszerservice.request("jIyajbe'");
// => Promise<...> resolves (with nothing), can now be retrieved using the fetch API
Készíts egy premium(text, quality) metódust a prémium felhasználóknak, amely lekér egy fordítást.
Ha egy fordítás NotAvailable, kérd a fordítást, és miután bekerült az API storage-ba, kérd le.
A metódus csak akkor adja vissza a fordítást, ha az eléri egy bizonyos quality küszöböt.
api.fetch sikeresen teljesül, a teljesülés előtt ellenőrizd a minőségetapi.fetch elutasít, helyette kérd a fordítástapi.request elutasít, add tovább a hibátservice.premium("jIyajbe'", 100);
// => Promise<...> resolves "I don't understand."
service.premium("'arlogh Qoylu'pu'?", 100);
// => Promise<...> rejects QualityThresholdNotMet()
service.premium("'arlogh Qoylu'pu'?", 40);
// => Promise<...> resolves "What time is it?"
Megjegyzés
A 'arlogh Qoylu'pu'? helyes fordítása How many times has it been heard?.
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) JavaScript nyelvet 37 fogalom159 feladat segítségével, valódi emberi mentorálással, mindez ingyen.