Třídy ES6 a async/await patří mezi nejdůležitější nové funkce v JavaScriptu. S třídami ES6 je psaní objektově orientovaného JavaScriptu jednodušší než kdykoli předtím. Totéž platí o psaní asynchronního JavaScriptu díky async/await. Naučte se používat tyto dvě funkce. Posuňte své znalosti JavaScriptu na další úroveň!
ES6, ES7, ES8 &Writing Modern JavaScript Part 1 (Scope, let, const, var).
ES6, ES7, ES8 &Writing Modern JavaScript Part 2 (Šablonové literály, Destructuring &Default Params).
ES6, ES7, ES8 a psaní moderního JavaScriptu, část 3 (roztažení, odpočinek, sady).
ES6, ES7, ES8 a moderní psaní JavaScriptu, část 4 (zahrnuje, podložky, smyčky a mapy).
ES6, ES7, ES8 &Writing Modern JavaScript, část 5 (WeakMap, WeakSet a Export &Import).
ES6, ES7, ES8 a psaní moderního JavaScriptu, část 6 (funkce šipek a sliby).
Asynchronní funkce a operátor čekání
Pokud máte hlubší znalosti JavaScriptu, zejména jeho asynchronní povahy, pravděpodobně také znáte zpětná volání. Pokud ne, zpětné volání je funkce, která se neprovede okamžitě, ale někdy v budoucnu. Funkce zpětného volání jsou často vyžadovány, když výsledky nemáte okamžitě k dispozici.
Obvykle to není velký problém, protože můžete použít funkci zpětného volání a počkat, až budete mít všechna potřebná data. Co když však existuje více než jen jedna asynchronní operace? Co když máte více asynchronních operací, z nichž jedna závisí na druhé? Představte si například tento hypotetický scénář.
Vstupte do pekla
Řekněme, že máte aplikaci. Tato aplikace načítá některá data z databáze. Než však budete moci tato data použít, musíte je ověřit a převést do jiného formátu. Po dokončení této konverze aplikace zobrazí výsledky. Háček je v tom, že všechny tyto kroky jsou asynchronní a jeden závisí na předchozím.
Je to děsivé? A co scénář, kde je počet asynchronních operací vyšší, například třikrát nebo čtyřikrát vyšší. V této situaci již zpětná volání nejsou nejlepší možností. Skončili byste u hnízdění na tolika úrovních, že byste k orientaci potřebovali mapu nebo návod. Jinak řečeno, skončili byste v pekle.
///
// Callback example:
getData((dataResponse, dataError) => {
// Verify the data
verifyData(dataResponse, (verifyResponse, verifyError) => {
// Convert the data
convertData((convertResponse, convertError) => {
// Finally display the data
displayData(convertResponse, (displayError) => {
// Handle any exceptions
console.log(displayError)
})
})
})
})
Zadejte Promises
Naštěstí existuje specifikace ES6, která zavedla některé užitečné funkce, které nám pomohou vypořádat se s podobnými scénáři. Nejprve přišly sliby. Sliby fungují velmi dobře. Stále však nejsou tím nejlepším a nejvybroušenějším řešením. Proč? Stále musíme používat zpětná volání uvnitř každých then()
. Dále musíme použít catch()
pro zpracování chyb.
Dalším problémem může být práce s více sliby. Představte si například opakování řady slibů za sebou, abyste získali data, která potřebujete, a dostali je ve formě, kterou potřebujete. Snadný? Ne tak moc. Zábava? Určitě ne. Případ bolesti hlavy? Pravděpodobně. Lepší řešení?
///
// Example of promises:
getData()
.then(dataResponse => {
// Verify the data
return verifyData()
.then(verifyResponse => {
// Convert the data
let convertedData = convertData(verifyResponse)
return convertedData
})
.then(result => {
// Finally display the data
displayData(result)
})
}).catch(() => {
// Handle any exceptions
handleErrors()
})
Zadejte async/wait
Po ES6 a ES7 přišel ES8. Tato specifikace zavedla dvě funkce, async
funkcí a await
operátor. Tyto dva byly řešením, které vývojáři JavaScriptu zoufale hledali. Asynchronní funkce spolu s await
, konečně výrazně zjednodušil práci s asynchronním kódem a sliby. Označili konec pekla zpětného volání.
Je důležité zmínit, že asynchronní funkce funguje nad rámec slibů. Používají sliby, aby vrátili výsledky. Přesto vypadají spíše jako normální funkce. Je proto lepší naučit se pracovat se sliby, než si začnete pohrávat s async
funkcí. Takže pokud nejste dobří se sliby, zapracujte nejprve na tom.
Další důležitá věc je, že async
funkce a await
operátor spolupracovat. Můžete použít await
pouze uvnitř async
funkce. Použití venku vyvolá chybu. A jaká je funkce nebo role await
operátor? Umožňuje vám pozastavit provádění asynchronní funkce a počkat, až bude slib vyřešen, buď jako splněný, nebo odmítnutý.
Od ES6 do ES8 – Syntaxe async/await
Použití async/await je velmi snadné. Jak jsem zmínil, nemůžete použít async
a await
odděleně. Nejprve tedy musíte použít async
spolu s funkcí. Díky tomu bude funkce asynchronní. Poté můžete použít await
, uvnitř této funkce. Počet použití await
není omezen . Můžete jej použít tolikrát, kolikrát potřebujete.
O syntaxi. Když deklarujete standardní funkci, async
operátor je na začátku deklarace před function
klíčové slovo (async function someFunction() {}
). V případě funkcí šipek zadejte async
operátor rovnítko (=
) a před závorkami (const someFunction = async () => {}
).
///
// Example of async/await syntax no.1: Standart function
async function someFunction() {
await ...
}
///
// Example of async/await syntax no.2: Arrow function
const someFunction = async () => {
await ...
}
///
// Example of async/await syntax no.3: Don't try this
function someFunction() {
await anotherFunction() // This will throw an error
}
To není vše. Můžete také použít async
funguje jako metody uvnitř tříd nebo objektů. V tomto scénáři je syntaxe podobná scénáři se standardní funkcí. async
klíčové slovo je před názvem metody (async someMethod() {}
). Jedna věc, kterou je třeba si zapamatovat... Konstruktory tříd a gettery/settery nemohou být asynchronní.
///
// Example of async/await syntax no.4: Object
// As an object's method
const someObj = {
async anotherFunction() {
// your code
}
}
///
// Example of async/await syntax no.5: Class
class SomeClass {
async anotherFunction() {
// your code
}
}
Nyní se vraťme k hypotetickému scénáři s aplikací a vykreslováním převedených dat. Místo používání slibů a více then()
metodami můžeme tento kód nahradit async
funkce a pár await
operátory. Jak můžete vidět na příkladu níže, umožní nám to udělat kód mnohem čistším a výrazně omezit vnořování.
///
// Example of async/await no.6:
// Create async function
async function appViewRender() {
// Use try block
try {
// Use await to wait for the data
const data = await getData()
// Use await to wait until data is verified
const verifiedData = await verifyData(data)
// Use await to wait until data is converted
const convertedData = await convertData(verifiedData)
// Finally display the data
displayData(convertedData)
} catch(error) {
// Use catch block to handle any exceptions
handleErrors()
}
}
Základy async/await
Jak již víte, async
funkce vždy vrátí slib. Chcete-li být konkrétnější, async
funkce vždy vrací hodnotu prostřednictvím slibu a jeho resolve()
metoda. Co když se vyskytne nějaký problém a slib bude odmítnut? Poté async
funkce vrátí odmítnutý slib. To znamená reject()
bude vrácena metoda s chybou namísto resolve()
.
///
// Example of async/await no.7: Async function vs regular promise
async function exampleAsyncFunction() {
return 'Foo'
}
// Async function returns a promise - we can use then()
exampleAsyncFunction.then(console.log)
// Outputs: Foo
///
// The same as using standard function explicitly returning a promise:
function functionWithPromise() {
return Promise.resolve('Foo')
}
functionWithPromise().then(console.log)
// Outputs: Foo
///
// The same as creating new promise:
const newPromise = () => new Promise((resolve, reject) => {
resolve('Foo')
reject('There was a problem with resolving your request.')
})
newPromise().then(console.log)
// Outputs: Foo
Jak jste mohli vidět na příkladu kódu č. 6 s appViewRender()
, použili jsme pár await
operátory uvnitř async
funkce. Každý z těchto operátorů říká funkci, že následující výraz je příslib. A každý z těchto operátorů také říká funkci, aby počkala, dokud nebude tento slib vyřešen.
To znamená, že pokud existuje nějaký await
funkce nebude pokračovat na další výraz, pokud nebude výraz s await
je vyřešeno. Teprve když k tomu dojde, bude funkce pokračovat ve vyhodnocování zbytku kódu uvnitř bloku. Co když používáte await
s hodnotou, která není příslibem?
V takovém případě to stejně skončí jako slib. JavaScript jej automaticky za běhu převede na příslib pomocí resolve()
metoda. Potom bude vyřešen nebo odmítnut jako jakýkoli jiný slib.
///
// Example of async/await no.8: Await operators, pausing and automatic conversion to promise
async function messageToHall() {
// Create a time stamp
console.log(`Stamp one: ${window.performance.now()}`)
// Create the first part of the message.
const firstPart = await 'Hello'
// Automatically converted to promise, to const a = await Promise.resolve('Hello')
// Pause the function for 2 seconds and then create the second part of the message.
const secondPart = await new Promise(resolve => setTimeout(
() => {
resolve('world')
}, 2000)
)
// Create the third part of the message.
const thirdPart = await 'Hal!'
// Automatically converted to promise, to const a = await Promise.resolve('Hal!')
// Create second time stamp
console.log(`Stamp two: ${window.performance.now()}`)
// Return the whole message in correct form
return `${firstPart} ${secondPart} ${thirdPart}`
}
messageToHall().then(console.log)
// Outputs:
// 'Stamp one: 340.9999999566935'
// 'Stamp two: 2343.899999978021'
// 'Hello world Hal!'
Jak můžete vidět na časových značkách v příkladu kódu výše, funkce byla skutečně pozastavena na 2 sekundy pomocí setTimeout()
metoda uvnitř slibu (const secondPart
). Teprve po těchto 2 sekundách funkce pokračovala a provedla zbytek kódu, včetně druhého časového razítka.
Asynchronní/čekající a chyby
Jedna skvělá věc na async
funkce je způsob, jakým zacházejí s chybami. Díky try ...catch
bloků, zpracování chyb probíhá synchronně. Každý slib je vyřešen a případná chyba se řeší jeden po druhém, aniž by došlo k porušení čehokoli. Můžeme to demonstrovat na jednoduchém příkladu.
Vytvořme funkci, která vrátí slib. Použijeme Math
náhodně vygenerovat buď 1 nebo 0 a použít toto číslo k vyřešení nebo odmítnutí slibu. Dále vytvoříme async
funkce s příkazy try...catch, které slíbí provedení funkce a zpracují výsledky.
///
// Example of async/await no.9: Async/await and handling errors
// Create function with promise that will be randomly either resolved or rejected
function resolveOrReject() {
return new Promise((resolve, reject) => {
// Randomly generate either 1 or 0
const shouldResolve = Math.round(Math.random() * 1)
// Resolve or reject the promise based on the value of shouldResolve
shouldResolve ? resolve('Promise resolved!') : reject('Promise rejected.')
})
}
// Create async function and use try block to handle case when promise is resolved and catch block when it is rejected
async function myAsyncFunction() {
try {
// Execute the resolveOrReject() function
const result = await resolveOrReject()
console.log(result)
} catch(error) {
// Handle any exceptions
console.log(error)
}
}
// Try your luck
myAsyncFunction()
// Outputs: 'Promise rejected.'
myAsyncFunction()
// Outputs: 'Promise resolved!'
myAsyncFunction()
// Outputs: 'Promise resolved!'
myAsyncFunction()
// Outputs: 'Promise rejected.'
myAsyncFunction()
// Outputs: 'Promise rejected.'
myAsyncFunction()
// Outputs: 'Promise resolved!'
Třídy
Další velkou změnou zavedenou v ES6 byly třídy. Před ES6 bylo možné objekty v JavaScriptu vytvářet pouze pomocí buď new Object()
nebo konstruktor funkcí. To je velký rozdíl od jiných objektově orientovaných programovacích jazyků, kde byste normálně používali třídu. ES6 to změňte. Nyní mohou třídy používat i vývojáři JavaScriptu.
Třídy ES6 jsou podobné další funkci představené v ES6, funkcím šipek. To znamená, že je to v podstatě syntaktický cukr. Na pozadí je to stále starý dobrý objekt kombinovaný s dědictvím založeným na prototypech, které znáte z minulosti. To však neznamená, že je to špatné, stejně jako v případě funkcí šipek.
Nové třídy ES6 mohou výrazně usnadnit práci vývojářům JavaScriptu. Syntaxe je přehlednější a čistší. To je věc osobního názoru, ale myslím si, že třídy usnadňují začátečníkům začít s objektově orientovaným JavaScriptem. Kód využívající třídy ES6 se mi zdá být čitelnější než kód využívající objekty a dědičnost na základě prototypů.
Syntaxe tříd ES6
Syntaxe tříd ES6 je jednoduchá. Začínáte s class
klíčové slovo následované názvem třídy. Jako první písmeno v názvu vždy používejte velká písmena. Poté následuje tělo tříd zabalených do složených závorek ({}
). Vlastnosti třídy jsou definovány v constructor()
metoda. constructor()
metoda je volitelná.
Pokud používáte constructor()
musí přijít jako první, na vrcholu třídy. Dále následuje všechny metody, které chcete, aby třída měla.
///
// Classes example no.1: Function constructor vs ES6 class
// Create Person object using function constructor
function Person(name, age, isLiving) {
this.name = name
this.age = age
this.isLiving = isLiving
}
// Add isAlive method to prototype of Person object
Person.prototype.isAlive = function() {
if (this.isLiving) {
console.log(`${this.name} is alive.`)
} else {
console.log(`${this.name} is dead.`)
}
}
// Create new instance of Person object
const joe = new Person('Joe', 59, true)
// Check if Joe is alive
joe.isAlive()
// Outputs: 'Joe is alive.'
// Using ES6 class:
// Create Person class
class Person {
// Define default properties
constructor(name, age, isLiving) {
this.name = name
this.age = age
this.isLiving = isLiving
}
// Create isAlive method to prototype of Person object
isAlive() {
if (this.isLiving) {
console.log(`${this.name} is alive.`)
} else {
console.log(`${this.name} is dead.`)
}
}
}
// Create new instance of Person class
const anthony = new Person('Anthony', 59, true)
// Check if Anthony is alive
anthony.isAlive()
// Outputs: 'Anthony is alive.'
Rozšíření tříd ES6
Stejně jako třídy v jiných objektově orientovaných programovacích jazycích lze třídy ES6 také rozšířit. Chcete-li vytvořit novou třídu rozšířením stávající třídy, znovu použijete class
klíčové slovo následované názvem třídy. Tělu třídy však předchází extends
klíčové slovo, za kterým následuje název třídy, kterou chcete rozšířit. Pak přichází tělo zabalené do složených rovnátek.
Když vytváříte třídu rozšířením další třídy, musíte pamatovat na použití super()
metoda v constructor()
. super()
metoda musí být první, hned na začátku constructor()
. Pokud má původní třída nějaké vlastnosti a chcete, aby nová třída tyto vlastnosti zdědila, musíte je předat jako argumenty oběma, constructor()
stejně jako super()
.
///
// Classes example no.2: Extending classes
// Create Human class
class Human {
// Define default properties
constructor(name, age) {
this.name = name
this.age = age
}
sayHi() {
console.log(`Hi, I am ${this.name}.`)
}
}
// Create Woman class by extending Human
class Woman extends Human {
// Define default properties
// Pass the name and age properties to constructor() and super() because we want the Woman class to inherit these properties
constructor(name, age) {
// Let Woman class inherit name and age properties from human
super(name, age)
this.gender = 'female'
}
tellYourGender() {
console.log(`I am a ${this.gender}.`)
}
}
// Create new instance of Woman class
const jackie = new Woman('Jackie', 26, true)
// Let Jackie introduce herself
jackie.sayHi()
// Outputs: 'Hi, I am Jackie.'
jackie.tellYourGender()
// Outputs: 'I am a female.'
// Create Man class by extending Human
class Man extends Human {
// Define default properties
// Pass the name and age properties to constructor() and super() because we want the Man class to inherit these properties
constructor(name, age) {
// Let Man class inherit name and age properties from human
super(name, age)
this.gender = 'male'
}
tellYourGender() {
console.log(`I am a ${this.gender}.`)
}
}
// Create new instance of Man class
const johny = new Man('Johny', 31, true)
// Let Johny introduce herself
johny.sayHi()
// Outputs: 'Hi, I am Johny.'
johny.tellYourGender()
// Outputs: 'I am a male.'
Epilolog:ES6, ES7, ES8 a psaní moderního JavaScriptu Pt7
Gratulujeme! Právě jste dokončili další díl série ES6, ES7, ES8 &Writing Modern JavaScript. Dnes jste se dozvěděli o dalších dvou nových funkcích, async/await a třídách. Nyní víte, jak používat třídu ES6 k psaní objektově orientovaného JavaScriptu. Také víte, jak udělat svůj kód asynchronním s async/await a vyhnout se peklu zpětného volání.
Nyní si dejte pauzu a nechte vše, co jste se dnes naučili, uklidnit. Dovolte svému mozku, aby vše zpracoval. Poté, jakmile se budete cítit svěží, projděte si znovu to, co jste se dnes naučili. Pohrajte si s příklady. Vyzkoušejte je, upravte je a poté si vytvořte vlastní. Pamatujte, že praxe je klíčem k opravdovému pochopení čehokoli. Takže jděte a napište nějaký kód.