ES6, ES7, ES8 a psaní moderního JavaScriptu Pt7 – Async/wait &Classes

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.