# JavaScript APIs & Promises - Anfänger Tutorial
## 📚 Inhaltsverzeichnis
1. [Promise Grundlagen](#promise-grundlagen)
2. [Async/Await](#asyncawait)
3. [Try/Catch Fehlerbehandlung](#trycatch-fehlerbehandlung)
4. [Promise Chaining mit .then()](#promise-chaining-mit-then)
5. [Fetch API](#fetch-api)
6. [Externe APIs nutzen](#externe-apis-nutzen)
7. [**Firebase Realtime Database**](#firebase-realtime-database) ⭐ **NEU**
8. [Praxisbeispiele](#praxisbeispiele)
---
## 🎯 Promise Grundlagen
### Was ist ein Promise?
- **Versprechen** für ein zukünftiges Ergebnis
- **Drei Zustände:** pending, fulfilled, rejected
- **Asynchrone Operationen** ohne Callback-Hell
### Basis Promise Syntax
```javascript
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
if (success) {
resolve("Erfolgreich!");
} else {
reject("Fehler aufgetreten!");
}
}, 1000);
});
```
### Einfaches Beispiel
```javascript
function getPromise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let success = true;
if (success) {
resolve("Hat geklappt!");
} else {
reject("Hat nicht geklappt!");
}
}, 1000);
});
}
```
---
## 🔄 Async/Await
### Vorteile von Async/Await
- **Sauberer Code** - keine Callback-Hell
- **Leserlicher** als Promise-Chains
- **Einfache Fehlerbehandlung** mit try/catch
### Syntax
```javascript
async function meineFunktion() {
try {
let ergebnis = await getPromise();
console.log(ergebnis);
} catch (error) {
console.error(error);
}
}
```
### Mehrere Promises abwarten
```javascript
async function mehrerePromises() {
try {
await getPromise1();
await getPromise2();
await getPromise3();
console.log("Alle Promises abgeschlossen!");
} catch (error) {
console.log("Ein Fehler ist aufgetreten:", error);
}
}
```
---
## 🛡️ Try/Catch Fehlerbehandlung
### Warum Try/Catch?
- **Fehler abfangen** ohne App-Crash
- **Benutzerfreundliche** Fehlermeldungen
- **Debugging** vereinfachen
### Beispiel mit mehreren Promises
```javascript
async function usePromise() {
try {
await getPromise1();
await getPromise2();
await getPromise3();
console.log("Alle erfolgreich!");
} catch (error) {
console.log("Fehler:", error);
} finally {
console.log("Immer ausgeführt");
}
}
```
---
## 🔗 Promise Chaining mit .then()
### Wann .then() verwenden?
- **Sequentielle Abarbeitung** von Promises
- **Verkettung** von Operationen
- **Alternative** zu async/await
### Basis .then() Syntax
```javascript
promise
.then(result => {
console.log("Erfolg:", result);
return nextPromise();
})
.then(result => {
console.log("Zweites Ergebnis:", result);
})
.catch(error => {
console.error("Fehler:", error);
});
```
### Praxisbeispiel
```javascript
function usePromise() {
getGoodPromise()
.then(result => {
console.log(result);
return getBadPromise();
})
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error);
});
}
```
---
## 🌐 Fetch API
### Was ist Fetch?
- **Moderne Alternative** zu XMLHttpRequest
- **Promise-basiert** für HTTP-Requests
- **Einfacher** als traditionelle AJAX-Calls
### Basis Fetch Syntax
```javascript
fetch(url)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
```
### Lokale JSON-Datei laden
```javascript
async function fetchDataJson() {
try {
let response = await fetch('db.json');
let data = await response.json();
console.log(data);
} catch (error) {
console.error('Fehler beim Laden:', error);
}
}
```
### Text-Datei laden
```javascript
async function fetchDataText() {
try {
let response = await fetch('h1.txt');
let text = await response.text(); // Wichtig: .text() statt .json()
document.getElementById("content").innerHTML = text;
} catch (error) {
console.error('Fehler:', error);
}
}
```
---
## 🍎 Externe APIs nutzen
### Fruityvice API
- **Kostenlose API** für Fruchtdaten
- **Keine Registrierung** erforderlich
- **Basis-URL:** `https://www.fruityvice.com/api/fruit/`
### API Endpunkte
- `GET /api/fruit/all` - Alle Früchte
- `GET /api/fruit/{name}` - Spezifische Frucht
- `GET /api/fruit/random` - Zufällige Frucht
- `PUT /api/fruit` - Neue Frucht hinzufügen
### Einzelne Frucht abrufen
```javascript
async function getFruit(fruitName) {
try {
let response = await fetch(`https://www.fruityvice.com/api/fruit/${fruitName}`);
let fruitData = await response.json();
console.log(fruitData);
return fruitData;
} catch (error) {
console.error('Frucht nicht gefunden:', error);
}
}
// Verwendung
getFruit('strawberry');
```
### Alle Früchte abrufen
```javascript
async function getAllFruits() {
try {
let response = await fetch('https://www.fruityvice.com/api/fruit/all');
let fruits = await response.json();
console.log('Anzahl Früchte:', fruits.length);
return fruits;
} catch (error) {
console.error('Fehler beim Laden aller Früchte:', error);
}
}
```
### Zufällige Frucht
```javascript
async function getRandomFruit() {
try {
let response = await fetch('https://www.fruityvice.com/api/fruit/random');
let randomFruit = await response.json();
console.log('Zufällige Frucht:', randomFruit.name);
return randomFruit;
} catch (error) {
console.error('Fehler bei zufälliger Frucht:', error);
}
}
```
---
## 🔥 Firebase Realtime Database
### Was ist Firebase Realtime Database?
- **NoSQL Cloud-Datenbank** von Google
- **Realtime-Synchronisation** zwischen Clients
- **REST API** für einfache Integration
- **Kostenlos** für kleine Projekte
### Setup und Konfiguration
1. **Firebase Projekt** erstellen: [console.firebase.google.com](https://console.firebase.google.com)
2. **Realtime Database** aktivieren
3. **Sicherheitsregeln** anpassen für Development:
```json
{
"rules": {
".read": true,
".write": true
}
}
```
### Firebase URL Struktur
```javascript
const BASE_URL = "https://PROJEKT-NAME-default-rtdb.REGION.firebasedatabase.app/";
// Beispiel:
const BASE_URL = "https://da-porjectapi-training-default-rtdb.europe-west1.firebasedatabase.app/";
```
### 📖 READ - Daten laden (GET)
```javascript
async function loadData(){
try {
let response = await fetch(BASE_URL + ".json");
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
let responseToJson = await response.json();
console.log("Firebase data:", responseToJson);
return responseToJson;
} catch (error) {
console.error("error:", error);
}
}
// Spezifischen Pfad laden
async function loadDataFromPath(path) {
try {
let response = await fetch(BASE_URL + path + ".json");
let data = await response.json();
return data;
} catch (error) {
console.error("Fehler beim Laden:", error);
}
}
// Verwendung
loadDataFromPath("users/user1"); // Lädt /users/user1
```
### ✍️ CREATE - Neue Daten hinzufügen (POST)
```javascript
async function postData(path = "", data = {}) {
try {
let response = await fetch(BASE_URL + path + ".json", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
let responseToJson = await response.json();
console.log("Posted data response:", responseToJson);
return responseToJson;
} catch (error) {
console.error("Error posting data:", error);
}
}
// Verwendung
postData("users", {
name: "Max Mustermann",
email: "max@example.com",
age: 25
});
```
### 🔄 UPDATE - Daten überschreiben (PUT)
```javascript
async function putData(path = "", data = {}) {
try {
let response = await fetch(BASE_URL + path + ".json", {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
let responseToJson = await response.json();
console.log("Data updated:", responseToJson);
return responseToJson;
} catch (error) {
console.error("Error updating data:", error);
}
}
// Verwendung
putData("users/user1", {
name: "Max Mustermann Updated",
email: "max.new@example.com"
});
```
### 🗑️ DELETE - Daten löschen (DELETE)
```javascript
async function deleteData(path = "") {
try {
let response = await fetch(BASE_URL + path + ".json", {
method: "DELETE"
});
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
console.log(`Data at path "${path}" deleted successfully`);
return true;
} catch (error) {
console.error("Error deleting data:", error);
return false;
}
}
// Verwendung
deleteData("users/user1"); // Löscht /users/user1
```
### 🔧 PATCH - Teilweise Updates
```javascript
async function patchData(path = "", data = {}) {
try {
let response = await fetch(BASE_URL + path + ".json", {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
let responseToJson = await response.json();
console.log("Data patched:", responseToJson);
return responseToJson;
} catch (error) {
console.error("Error patching data:", error);
}
}
// Verwendung - nur bestimmte Felder ändern
patchData("users/user1", {
age: 26 // Nur age wird geändert, rest bleibt
});
```
### 📝 Vollständiges Firebase Beispiel
#### index.html
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Firebase Storage</title>
<script src="remoteStorage.js"></script>
</head>
<body onload="onloadFunc()">
<div id="content"></div>
</body>
</html>
```
#### remoteStorage.js
```javascript
const BASE_URL = "https://da-porjectapi-training-default-rtdb.europe-west1.firebasedatabase.app/";
async function loadData(){
try {
let response = await fetch(BASE_URL + ".json");
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
let responseToJson = await response.json();
console.log("Firebase data:", responseToJson);
return responseToJson;
} catch (error) {
console.error("error:", error);
}
}
async function postData(path = "", data = {}) {
try {
let response = await fetch(BASE_URL + path + ".json", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data)
});
let responseToJson = await response.json();
console.log("Posted data response:", responseToJson);
return responseToJson;
} catch (error) {
console.error("Error posting data:", error);
}
}
async function deleteData(path = "") {
try {
let response = await fetch(BASE_URL + path + ".json", {
method: "DELETE"
});
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
console.log(`Data at path "${path}" deleted successfully`);
return true;
} catch (error) {
console.error("Error deleting data:", error);
return false;
}
}
function onloadFunc(){
console.log("App started...");
loadData();
postData("", {"banana": "rama"});
deleteData("/name/");
}
```
### Firebase URL-Struktur verstehen
```javascript
// Basis URL
https://PROJEKT-default-rtdb.REGION.firebasedatabase.app/
// Pfade hinzufügen
/users.json // Alle User
/users/user1.json // Spezifischer User
/users/user1/name.json // Nur Name von user1
/products/electronics.json // Elektronik-Produkte
// Beispiele:
loadData("users") // GET /users.json
postData("users", userData) // POST /users.json
putData("users/user1", data) // PUT /users/user1.json
deleteData("users/user1") // DELETE /users/user1.json
```
### Firebase vs. andere APIs
| Feature | Firebase | REST API | GraphQL |
|---------|----------|----------|---------|
| **Setup** | Einfach | Mittel | Komplex |
| **Realtime** | ✅ Ja | ❌ Nein | ✅ Ja |
| **Offline** | ✅ Ja | ❌ Nein | ❌ Nein |
| **Kosten** | Kostenlos* | Variiert | Variiert |
| **Skalierung** | Automatisch | Manuell | Manuell |
---
## 💡 Praxisbeispiele
### Beispiel 1: Frucht-Suche
```javascript
async function searchFruit() {
let fruitName = document.getElementById('fruitInput').value;
try {
let response = await fetch(`https://www.fruityvice.com/api/fruit/${fruitName}`);
if (!response.ok) {
throw new Error('Frucht nicht gefunden');
}
let fruit = await response.json();
displayFruit(fruit);
} catch (error) {
console.error('Fehler:', error);
document.getElementById('result').innerHTML = 'Frucht nicht gefunden!';
}
}
function displayFruit(fruit) {
let html = `
<h2>${fruit.name}</h2>
<p>Familie: ${fruit.family}</p>
<p>Kalorien: ${fruit.nutritions.calories}</p>
<p>Zucker: ${fruit.nutritions.sugar}g</p>
`;
document.getElementById('result').innerHTML = html;
}
```
### Beispiel 2: Firebase Todo-App
```javascript
// Todo hinzufügen
async function addTodo(todoText) {
let todoData = {
text: todoText,
completed: false,
created: Date.now()
};
let result = await postData("todos", todoData);
console.log("Todo hinzugefügt:", result.name);
return result;
}
// Alle Todos laden
async function loadTodos() {
let todos = await loadDataFromPath("todos");
displayTodos(todos);
}
// Todo als erledigt markieren
async function completeTodo(todoId) {
await patchData(`todos/${todoId}`, { completed: true });
loadTodos(); // Neu laden
}
// Todo löschen
async function deleteTodo(todoId) {
await deleteData(`todos/${todoId}`);
loadTodos(); // Neu laden
}
function displayTodos(todos) {
let html = '<h2>Meine Todos:</h2><ul>';
for (let id in todos) {
let todo = todos[id];
let status = todo.completed ? '✅' : '❌';
html += `
<li>
${status} ${todo.text}
<button onclick="completeTodo('${id}')">Erledigt</button>
<button onclick="deleteTodo('${id}')">Löschen</button>
</li>
`;
}
html += '</ul>';
document.getElementById('todoList').innerHTML = html;
}
```
### Beispiel 3: User Management mit Firebase
```javascript
// Benutzer registrieren
async function registerUser(userData) {
try {
let result = await postData("users", {
name: userData.name,
email: userData.email,
registered: Date.now(),
active: true
});
console.log("User registriert:", result.name);
return result.name; // Firebase generierte ID
} catch (error) {
console.error("Registrierung fehlgeschlagen:", error);
}
}
// Benutzer-Profil laden
async function getUserProfile(userId) {
try {
let profile = await loadDataFromPath(`users/${userId}`);
return profile;
} catch (error) {
console.error("Profil nicht gefunden:", error);
}
}
// Benutzer-Profil aktualisieren
async function updateUserProfile(userId, updates) {
try {
await patchData(`users/${userId}`, updates);
console.log("Profil aktualisiert");
} catch (error) {
console.error("Update fehlgeschlagen:", error);
}
}
// Alle aktiven Benutzer laden
async function getActiveUsers() {
try {
let allUsers = await loadDataFromPath("users");
let activeUsers = {};
for (let id in allUsers) {
if (allUsers[id].active) {
activeUsers[id] = allUsers[id];
}
}
return activeUsers;
} catch (error) {
console.error("Fehler beim Laden aktiver User:", error);
}
}
```
### Beispiel 4: Mehrere API-Calls kombinieren
```javascript
async function compareFruits(fruit1, fruit2) {
try {
let [data1, data2] = await Promise.all([
fetch(`https://www.fruityvice.com/api/fruit/${fruit1}`).then(r => r.json()),
fetch(`https://www.fruityvice.com/api/fruit/${fruit2}`).then(r => r.json())
]);
console.log(`${fruit1}: ${data1.nutritions.calories} Kalorien`);
console.log(`${fruit2}: ${data2.nutritions.calories} Kalorien`);
let winner = data1.nutritions.calories < data2.nutritions.calories ? fruit1 : fruit2;
console.log(`${winner} hat weniger Kalorien!`);
} catch (error) {
console.error('Fehler beim Vergleich:', error);
}
}
// Verwendung
compareFruits('apple', 'banana');
```
---
## 🚀 Erweiterte Techniken
### Promise.all() für parallele Requests
```javascript
async function loadMultipleFruits() {
let fruitNames = ['apple', 'banana', 'orange'];
try {
let promises = fruitNames.map(name =>
fetch(`https://www.fruityvice.com/api/fruit/${name}`)
.then(response => response.json())
);
let results = await Promise.all(promises);
console.log('Alle Früchte geladen:', results);
} catch (error) {
console.error('Fehler bei einem Request:', error);
}
}
```
### Timeout für Requests
```javascript
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
}
async function getFruitWithTimeout(fruitName) {
try {
let response = await fetchWithTimeout(
`https://www.fruityvice.com/api/fruit/${fruitName}`,
3000
);
let fruit = await response.json();
return fruit;
} catch (error) {
console.error('Request zu langsam oder fehlgeschlagen:', error);
}
}
```
### Firebase Realtime Updates (Advanced)
```javascript
// Server-Sent Events für Realtime Updates
function listenToFirebaseChanges(path) {
const eventSource = new EventSource(
`${BASE_URL}${path}.json?sse=true`
);
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('Daten geändert:', data);
// UI aktualisieren
updateUI(data);
};
eventSource.onerror = function(error) {
console.error('SSE Fehler:', error);
};
return eventSource;
}
// Verwendung
const listener = listenToFirebaseChanges('todos');
// listener.close(); // Verbindung schließen
```
---
## 🔧 Debugging-Tipps
### Console.log richtig nutzen
```javascript
async function debugFetch() {
console.log('1. Starte Request...');
try {
let response = await fetch('https://www.fruityvice.com/api/fruit/apple');
console.log('2. Response erhalten:', response.status);
let data = await response.json();
console.log('3. Daten geparst:', data);
} catch (error) {
console.error('4. Fehler aufgetreten:', error);
}
}
```
### Firebase Debugging
```javascript
// Firebase Response Status prüfen
async function debugFirebaseRequest() {
try {
let response = await fetch(BASE_URL + ".json");
console.log("Status:", response.status);
console.log("OK:", response.ok);
console.log("Headers:", response.headers);
if (response.status === 401) {
console.error("Firebase Berechtigung fehlt!");
}
let data = await response.json();
console.log("Data:", data);
} catch (error) {
console.error("Firebase Fehler:", error);
}
}
```
### Häufige Fehlerquellen
- **CORS-Probleme** bei externen APIs
- **Falsche URL** oder Endpoint
- **Firebase Sicherheitsregeln** zu restriktiv
- **Vergessenes await** bei async Funktionen
- **response.json()** statt **response.text()** bei Text-Dateien
- **Falsche HTTP-Methoden** (GET, POST, PUT, DELETE)
---
## 📊 Performance & Best Practices
### Caching implementieren
```javascript
// Einfacher Memory Cache
const cache = new Map();
async function getCachedData(url, cacheTime = 5 * 60 * 1000) { // 5 Minuten
const now = Date.now();
const cached = cache.get(url);
if (cached && (now - cached.timestamp) < cacheTime) {
console.log('Aus Cache geladen');
return cached.data;
}
console.log('Neue Anfrage');
const response = await fetch(url);
const data = await response.json();
cache.set(url, {
data: data,
timestamp: now
});
return data;
}
```
### Rate Limiting
```javascript
// Request Queue für Rate Limiting
class RequestQueue {
constructor(maxConcurrent = 3, delay = 1000) {
this.queue = [];
this.running = 0;
this.maxConcurrent = maxConcurrent;
this.delay = delay;
}
async add(fetchPromise) {
return new Promise((resolve, reject) => {
this.queue.push({
fetchPromise,
resolve,
reject
});
this.process();
});
}
async process() {
if (this.running >= this.maxConcurrent || this.queue.length === 0) {
return;
}
this.running++;
const { fetchPromise, resolve, reject } = this.queue.shift();
try {
const result = await fetchPromise();
resolve(result);
} catch (error) {
reject(error);
}
this.running--;
setTimeout(() => {
this.process();
}, this.delay);
}
}
// Verwendung
const requestQueue = new RequestQueue(2, 500); // Max 2 parallel, 500ms delay
async function queuedRequest(url) {
return requestQueue.add(() => fetch(url).then(r => r.json()));
}
```
---
## 📝 Zusammenfassung
### Key Takeaways
- **Promises** vereinfachen asynchrone Programmierung
- **Async/Await** macht Code lesbarer als .then()
- **Try/Catch** für saubere Fehlerbehandlung
- **Fetch API** für moderne HTTP-Requests
- **Firebase** bietet einfache Cloud-Database
- **Externe APIs** erweitern App-Funktionalität
### Firebase vs. Externe APIs
| Aspekt | Firebase | Externe APIs |
|--------|----------|--------------|
| **Setup** | Einfach | Variiert |
| **Kosten** | Kostenlos (Limits) | Oft kostenpflichtig |
| **Kontrolle** | Eigene Daten | Fremd-kontrolliert |
| **Offline** | Ja | Nein |
| **Realtime** | Ja | Selten |
### Best Practices
- **Immer Fehlerbehandlung** implementieren
- **Await nicht vergessen** bei async Funktionen
- **Loading States** für bessere UX
- **API-Limits** und **Rate Limiting** beachten
- **Caching** für Performance
- **Firebase Sicherheitsregeln** für Production
### Nächste Schritte
- **Firebase Authentication** implementieren
- **Progressive Web Apps (PWA)** entwickeln
- **Service Workers** für Offline-Funktionalität
- **WebSockets** für Realtime-Features
- **GraphQL** als Alternative zu REST
---
## 🔗 Nützliche Links
### Dokumentation
- [MDN Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
- [Firebase Dokumentation](https://firebase.google.com/docs)
- [Promises MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
### APIs zum Üben
- [JSONPlaceholder](https://jsonplaceholder.typicode.com/) - Fake REST API
- [Fruityvice](https://www.fruityvice.com/) - Frucht-Daten
- [Cat Facts API](https://catfact.ninja/) - Katzen-Fakten
- [Random User API](https://randomuser.me/) - Zufällige User-Daten
### Tools
- [Postman](https://www.postman.com/) - API Testing
- [Firebase Console](https://console.firebase.google.com/) - Firebase Management
- [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools) - Browser Debugging