Optimieren Sie Ihren JavaScript-Code mit Warten auf höchstem Niveau
Verhindern Sie, dass Ihr JavaScript-Code hängen bleibt oder Fehler zurückgibt, während Sie auf externe Ressourcen oder lang laufende Prozesse warten.
JavaScript ist eine beliebte Programmiersprache, die als synchrone Single-Thread-Sprache begann. Das bedeutet, dass ein Schritt nach dem anderen ausgeführt wird, ohne darauf zu warten, externe Ressourcen abzurufen oder langwierige Berechnungen oder Prozesse durchzuführen. Wenn das Skript eine solche Ressource oder Berechnung benötigt, führt dieses synchrone Verhalten zu einem Fehler. Dadurch wird die Ausführung aller anderen Prozesse in der Warteschlange blockiert, unabhängig davon, ob sie von dem Schritt abhängig waren, der den Fehler verursacht hat.
Aber vor einiger Zeit hat JavaScript eine neue Funktion eingeführt, die es ermöglicht, nur auf Code zu warten, der eine externe Ressource zum Laden oder einen langwierigen Prozess erfordert, während der Rest des Codes verarbeitet und gerendert wird. Dieses asynchrone Verhalten wird durch die Verwendung von Rückrufen oder Versprechen erreicht, die auf Funktionsebene funktionieren.
Was sind Rückrufe und Versprechen?
Ich werde diese Konzepte mithilfe von Code erklären. Wenn Sie bereits wissen, was Rückrufe und Versprechen sind, können Sie gerne zum Abschnitt „Warten auf oberster Ebene“ und zur Beispielanwendung springen.
Rückrufe
Bei einem Rückruf wird eine Funktion als Argument an eine andere Funktion übergeben; Daher ist das zweite Argument in der Funktion addEventListener
unten eine Rückruffunktion. Dieser Rückruf wartet auf das Eintreten des ersten Arguments und führt – erst dann – den Hauptteil des zweiten Arguments aus.
const x = document.getElementsByTagName('Button');
x[0].addEventListener('click',() =>{alert("I was clicked")})
Dieses Warteverhalten macht den Code asynchron. Dies unterscheidet sich von synchronem Code, bei dem Aufgaben nacheinander ausgeführt werden, ohne auf den Download einer Ressource oder die Verarbeitung einer langwierigen Berechnung zu warten. Beachten Sie jedoch, dass nicht alle Rückruffunktionen asynchron sind.
Versprechen
Versprechen ähneln Rückrufen in dem Sinne, dass sie dem zurückgegebenen Objekt eine Funktion hinzufügen. Es gibt jedoch Unterschiede zwischen Rückrufen und Versprechen. Versprechen sind hauptsächlich für asynchrone Funktionen konzipiert. Sie haben nur ein Argument und eine .then()
-Funktion wird nach den Ergebnissen verkettet, sobald ihr einziges Argument zurückgegeben wird. Außerdem können mehrere Funktionen .then()
und catch()
damit verbunden sein.
fetch('www.xyz.com/api')
.then((res)=>{let x = res.data; //do something with received data})
.catch((err)=>{console.log(err)})
Versprechen verwenden eine Ereigniswarteschlange und halten sich strikt an die Reihenfolge, in der die Aufgaben verkettet sind.
Asynchron/warten
Async/await ist die syntaktische Modifikation von Versprechen, um eine Verkettung zu vermeiden. Dadurch wird der Code viel sauberer und verständlicher. Das Schlüsselwort await
hält den Code an, bis das Versprechen gelöst oder abgelehnt wird.
async function asyncwaitcode(){
let getData = await axios('www.xyzdata.org/api')
console.log(getData.data)
}
Was ist Top-Level-Award?
Alle oben genannten Beispiele machen die Schritte in Funktionsblöcken asynchron und keines funktioniert auf modularer Ebene.
Auf modularer Ebene kann jedoch asynchrones Verhalten erreicht werden. Der Zweck der Verwendung von Wait auf oberster Ebene besteht darin, Modulen die asynchrone Initialisierung ihres Namespace zu ermöglichen, bevor sie den Verbraucher darüber informieren, dass die Auswertung des Moduls abgeschlossen ist.
Die folgende Beispiel-App verwendet Wait auf oberster Ebene, um zu zeigen, wie dies funktioniert.
Über die App
Diese App ruft die wichtigsten Nachrichtendaten von einer Nachrichten-API ab und rendert sie lokal im Browser. Diese App verfügt außerdem über eine Suchfunktion, sodass Benutzer Nachrichtendaten zu einem gesuchten Begriff abrufen können. Bevor ich anfange, gibt es ein paar Vorbehalte zu beachten:
- Top-Level-Await wird in Knotenversion 13.3 und höher unterstützt, und keine dieser Versionen verfügt derzeit über LTS (Langzeitunterstützung).
- Top-Level-Await wird nur im ECMAScript-Modul unterstützt, und Node.js und Express verwenden CommonJS-Module. In Node.js wurde jedoch Unterstützung für das ECMAScript-Modul hinzugefügt. Anstatt also
require
zum Abrufen der Module zu verwenden, verwende ichimport
in der gesamten App. CommonJS unterstützt kein Warten auf oberster Ebene. - Das Warten auf oberster Ebene funktioniert nicht mit dem benannten Export. es funktioniert nur mit Standardexporten.
- In Versionen vor Node.js 14.x ist das Warten auf oberster Ebene nicht sofort einsatzbereit. Stattdessen müssen Sie zum Ausführen das Warte-Flag
--harmony
der obersten Ebene der Google V8-Engine verwenden. Es wird jedoch in 14.x und höher vollständig unterstützt (d. h. es läuft ohne Flags). - Das Warten auf oberster Ebene funktioniert nicht mit klassischen Skripten und nicht asynchronen Funktionen.
- Zirkuläre Modulabhängigkeiten könnten zu einem Deadlock führen.
Voraussetzungen
- Node.js Version 13.3 oder höher
- npm
- API-Schlüssel für die News-APIs (siehe unten)
Erstellen Sie die App
Erstellen Sie zunächst ein neues Verzeichnis mit dem Namen
toplevelawait
:$ mkdir toplevelawait
Führen Sie
npm init
aus, um einepackage.json
-Datei zu erstellen:$ npm init
Fügen Sie
"type": "module"
zupackage.json
hinzu, um Unterstützung für ECMAScript-Module hinzuzufügen:"author": "","license": "ISC","type": "module",
-
Erstellen Sie einen Ordner
src
im Ordnertoplevelawat
. Erstellen Sie im Ordnersrc
ein Verzeichnisviews
, um die eingebetteten JavaScript-Dateien (.ejs) zu speichern.$ mkdir -p src/views
Erstellen Sie zwei Dateien –
app.mjs
undexp.mjs
– im Ordnersrc
. Beachten Sie, dass die Dateierweiterung.mjs
ist und nicht nur.js
, was darauf hinweist, dass Sie ECMA-Module verwenden.$ touch app.mjs$ touch exp.mjs$ ls -1 srcapp.mjsexp.mjs
Als nächstes fügen Sie die Abhängigkeiten
axios
,ejs
undexpress
hinzu:$ npm install axios ejs express --save
Fügen Sie Folgendes in die Datei
exp.mjs
ein:import express from "express"export const exp = await express();
Beachten Sie, dass hierbei das Schlüsselwort await
ohne das Schlüsselwort async
verwendet wird. Bei dieser Wartezeit wird darauf gewartet, dass die Express-Instanz initialisiert wird, bevor sie in andere Module exportiert wird. Sie können dieses Beispiel verwenden, um auf die Initialisierung der Instanzen zu warten, bevor Sie zu Code wechseln, der von der erwarteten Ressource abhängt.
Wenn ein Modul ein Warten auf oberster Ebene enthält, wird die Ausführung dieses Moduls und des übergeordneten Moduls angehalten, bis das Versprechen aufgelöst ist. Alle Geschwister werden weiterhin auf die übliche, synchrone Weise ausgeführt.
Beachten Sie, dass das Laden von Modulen in Node.js ebenfalls synchron erfolgt, was bedeutet, dass nicht auf das Laden einer Ressource gewartet wird. Sie erhalten eine Fehlermeldung. Sie können jedoch asynchrones Verhalten erreichen, indem Sie das Schlüsselwort await
vor der Ressource platzieren, die geladen wird oder eine Verarbeitung ausführt.
Fügen Sie die Nachrichten-APIs hinzu
Diese App nutzt zwei frei verfügbare Nachrichten-APIs, um Daten abzurufen. Die Verwendung von zwei APIs unterstützt das Fallback-Abhängigkeitsverhalten. Wenn eine API die Daten nicht abrufen kann, erhält sie die andere. Beide APIs verwenden API-Schlüssel:
- Nachrichten-API
- GNews-API
Fügen Sie den folgenden Code in die Datei app.mjs
ein. In diesem ersten Abschnitt werden die in exp.js
initialisierten Axios- und Express-Instanzen importiert. Im nächsten Teil wird die Ansichts-Engine so eingestellt, dass EJS-Dateien in einem Browser angezeigt werden:
import { exp } from "./exp.mjs";
import axios from "axios"
exp.set("view engine","ejs");
// dependency fall back
let response = "";
let site = true;
try{
response = await axios('https://newsapi.org/v2/top-headlines?country=us&apiKey=your-api-key');
}
catch{
response = await axios("https://gnews.io/api/v3/top-news?token=your-api-key");
site = false;
}
// Get top news
exp.get('/',function(req,res){
let response0 = response.data.articles
res.render('main.ejs',{response0: response0, site:site})
})
// search news
exp.get('/search', function(req,res){
res.render("searchnews.ejs")
})
exp.get('/result', async(req, res)=>{
let x = req.query.newtitlesearch;
let response1 = {}
let data = {}
try{
let url = 'https://newsapi.org/v2/everything?q='+x+'&apiKey=your-api-key'
response1 = await axios(url);
}
catch{
let url = 'https://gnews.io/api/v3/search?q='+x+'&token=your-api-key'
response1 = await axios(url)
}
res.render('result.ejs', {response1: response1.data.articles, site: site})
})
exp.listen(3000)
Der wichtigste Teil kommt als Nächstes: der try- und catch-Block, der mithilfe der obersten Warteebene darauf wartet, dass Axios die Daten von der API abruft, und zwar aus irgendeinem Grund Wenn Axios die Daten nicht von der ersten API abrufen kann, verwendet die App die zweite API, um die Daten abzurufen. Sobald es die Daten von der API erhält, rendert Express sie auf der Hauptseite:
try{
response = await axios('https://newsapi.org/v2/top-headlines?country=us&apiKey=your-api-key');
}
catch{
response = await axios("https://gnews.io/api/v3/top-news?token=your-api-key");
}
Danach gibt es einen weiteren Expressweg, der Benutzer zu einem Suchformular führt, in dem sie nach Nachrichten suchen können, die sie interessieren:
// search news
exp.get('/search', function(req,res){
res.render("../src/view/searchnews.ejs")
})
Schließlich zeigt eine weitere Expressroute Ergebnisse der Suche an:
exp.get('/result', async(req,res)=>{
let x = req.query.newtitlesearch;
let response1 = {}
let data = {}
try{
let url = 'https://newsapi.org/v2/everything?q='+x+'&apiKey=your-api-key'
response1 = await axios(url);
}
catch{
let url = 'https://gnews.io/api/v3/search?q='+x+'&token=your-api-key'
response1 = await axios(url)
}
res.render('../src/view/result.ejs', {response1: response1.data.articles , site: site})
})
Schreiben Sie die Frontend-Seiten
Der letzte Teil der App schreibt die vier .ejs-HTML-Dateien für die Frontend-Seiten. Speichern Sie diese Dateien im Ordner views
:
//header.ejs
<!DOCTYPE html>
<head>
<title>newapiapp</title>
<link type="text/css" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" >
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">News app</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="https://opensource.com/">Main</a></li>
<li><a href="https://opensource.com/search">Search</a></li>
</ul>
</div>
</div>
</nav>
//main.ejs
<%- include('header');%>
<%let rows = response0%>
<%let siterep = site%>
<div name "container">
<div class="row text-center" style="display:flex; flex-wrap:wrap">
<% for(let i = 0; i < rows.length; i++){%>
<div class="col-md-3 col-sm-6 ">
<div class="thumbnail" >
<a href="https://opensource.com/%3C%25-rows%5Bi%5D.url%20%25%3E">
<img src = "<%= siterep ? rows[i].urlToImage : rows[i].url %>">
</a>
</div>
<div><%= rows[i].title%></div>
</div>
<% } %>
</div>
</div>
//Searchnews.ejs
<%- include('header');%>
<h1>Search news </h1>
<form action="https://opensource.com/result" method="Get">
<iput type ="text" placeholder="news title search" name="newtitlesearch"></input>
<input type="submit" placeholder="submit"></input>
</form>
//result.ejs
<%- include('header');%>
<%let rows = response1%>
<%let siterep = site%>
<div name "container">
<div class="row text-center" style="display:flex; flex-wrap:wrap">
<% for(let i = 0; i < rows.length; i++){%>
<div class="col-md-3 col-sm-6 ">
<div class="thumbnail" >
<a href="https://opensource.com/%3C%25-rows%5Bi%5D.url%20%25%3E">
<img src = "<%= siterep ? rows[i].urlToImage : rows[i].url %>">
</a>
</div>
<div><%= rows[i].title%></div>
</div>
<% } %>
</div>
</div>
Führen Sie die App aus
Jetzt ist die App fertig und Sie können sie ausprobieren.
Wenn Sie eine Node.js-Version von v13.3 bis v14.0 verwenden, führen Sie das Programm mit Folgendem aus:
$ node --harmony-top-level-await app.js
Wenn Sie Node.js v14 und höher verwenden, müssen Sie das Flag --harmony
von V8 nicht verwenden:
$ node app.js
Wenn Sie diese App erfolgreich erstellt haben, herzlichen Glückwunsch! Sie haben eine neue JavaScript-Funktion kennengelernt.
Weitere Verwendungsmöglichkeiten von Top-Level-Await finden Sie im ECMAScript TC39-Top-Level-Await-Vorschlag.
Der Code für dieses Wartebeispiel der obersten Ebene mit Node.js von Sumaira Ahmad ist Open Source unter einer CC BY 4.0-Lizenz.