โ†Back to PAI Training
Hands-on LabIntermediateโฑ๏ธ75 minutes๐ŸŽจ Whiteboard Required

Async PAI Interactions: Mastering Promise-Based AI Communication

Build responsive Personal AI systems using JavaScript's async/await, promises, and event-driven architecture for seamless user interactions

Async PAI Interactions: Mastering Promise-Based AI Communication

"In the symphony of human-AI interaction, timing is everything. Async programming ensures the music never stops."

Exercise Overview

Modern Personal AI systems must be responsive, non-blocking, and capable of handling multiple simultaneous interactions. This exercise teaches you to build PAI systems using JavaScript's powerful async capabilities, creating smooth, real-time experiences that feel natural to users.

What You'll Build

  1. Streaming PAI Response System - Real-time AI responses with live typing indicators
  2. Multi-Modal Input Processor - Handle voice, text, and gesture inputs concurrently
  3. Reactive Dashboard - Live PAI performance metrics and user engagement analytics
  4. Error-Resilient Communication - Graceful handling of network issues and AI service failures

๐ŸŽฏ Learning Checkpoint: Async JavaScript Fundamentals

Quick Quiz: Test Your Understanding

Question 1: What's the difference between Promise.all() and Promise.allSettled() for PAI systems?

Click to reveal answer

Answer:

  • Promise.all() fails fast - if any PAI service fails, the entire operation fails
  • Promise.allSettled() waits for all services to complete, giving you results for successful operations even if some fail

PAI Use Case:

1// Promise.all() - stops at first failure
2const [voiceResponse, textResponse, visualResponse] = await Promise.all([
3 generateVoiceResponse(userInput),
4 generateTextResponse(userInput),
5 generateVisualResponse(userInput) // If this fails, you lose everything
6]);
7
8// Promise.allSettled() - resilient to failures
9const responses = await Promise.allSettled([
10 generateVoiceResponse(userInput),
11 generateTextResponse(userInput),
12 generateVisualResponse(userInput)
13]);
14
15// Still get successful responses even if one fails
16const successfulResponses = responses
17 .filter(result => result.status === 'fulfilled')
18 .map(result => result.value);

๐Ÿš€ Exercise 1: Streaming PAI Response System

Challenge: Build a Real-Time AI Response Stream

Create a PAI system that streams responses in real-time, similar to how ChatGPT displays responses token by token.

Code Template

1// PAI Response Streaming System
2class StreamingPAIEngine {
3 constructor(apiEndpoint) {
4 this.apiEndpoint = apiEndpoint;
5 this.activeStreams = new Map();
6 this.responseCache = new Map();
7 }
8
9 // TODO: Implement streaming response with proper async handling
10 async *streamResponse(userInput, options = {}) {
11 const streamId = this.generateStreamId();
12
13 try {
14 // TODO: Initialize the stream with loading indicator
15 yield { type: 'start', streamId, timestamp: Date.now() };
16
17 // TODO: Simulate API call with chunks of response
18 const response = await this.fetchPAIResponse(userInput, options);
19
20 // TODO: Stream the response word by word
21 const words = response.split(' ');
22
23 for (let i = 0; i < words.length; i++) {
24 // TODO: Yield each word with appropriate delay
25 await this.simulateTypingDelay();
26
27 yield {
28 type: 'chunk',
29 streamId,
30 content: words[i] + ' ',
31 progress: (i + 1) / words.length,
32 timestamp: Date.now()
33 };
34 }
35
36 // TODO: Signal completion
37 yield {
38 type: 'complete',
39 streamId,
40 fullResponse: response,
41 timestamp: Date.now()
42 };
43
44 } catch (error) {
45 // TODO: Handle streaming errors gracefully
46 yield {
47 type: 'error',
48 streamId,
49 error: error.message,
50 timestamp: Date.now()
51 };
52 } finally {
53 // TODO: Cleanup stream resources
54 this.activeStreams.delete(streamId);
55 }
56 }
57
58 // TODO: Implement concurrent PAI processing
59 async processMultipleInputs(inputs) {
60 console.log(`Processing ${inputs.length} inputs concurrently...`);
61
62 // TODO: Use Promise.allSettled for resilient processing
63 const promises = inputs.map(async (input, index) => {
64 try {
65 // TODO: Add jitter to prevent thundering herd
66 const delay = Math.random() * 1000;
67 await this.delay(delay);
68
69 const response = await this.fetchPAIResponse(input.text, input.options);
70
71 return {
72 index,
73 status: 'success',
74 input: input.text,
75 response,
76 processingTime: Date.now() - input.timestamp
77 };
78 } catch (error) {
79 return {
80 index,
81 status: 'error',
82 input: input.text,
83 error: error.message,
84 processingTime: Date.now() - input.timestamp
85 };
86 }
87 });
88
89 // TODO: Wait for all processing to complete
90 const results = await Promise.allSettled(promises);
91
92 // TODO: Separate successful and failed results
93 const successful = results.filter(r => r.status === 'fulfilled');
94 const failed = results.filter(r => r.status === 'rejected');
95
96 console.log(`โœ… ${successful.length} successful, โŒ ${failed.length} failed`);
97
98 return { successful, failed, total: inputs.length };
99 }
100
101 // TODO: Implement smart response caching
102 async getCachedOrFetchResponse(userInput, options = {}) {
103 const cacheKey = this.generateCacheKey(userInput, options);
104
105 // TODO: Check cache first
106 if (this.responseCache.has(cacheKey)) {
107 console.log('๐Ÿ’พ Cache hit for:', userInput.substring(0, 30) + '...');
108 return {
109 response: this.responseCache.get(cacheKey),
110 fromCache: true,
111 timestamp: Date.now()
112 };
113 }
114
115 // TODO: Fetch fresh response
116 console.log('๐ŸŒ Fetching fresh response for:', userInput.substring(0, 30) + '...');
117
118 try {
119 const response = await this.fetchPAIResponse(userInput, options);
120
121 // TODO: Cache the response with TTL
122 this.responseCache.set(cacheKey, response);
123
124 // TODO: Implement cache eviction after 5 minutes
125 setTimeout(() => {
126 this.responseCache.delete(cacheKey);
127 console.log('๐Ÿ—‘๏ธ Evicted cache for:', cacheKey);
128 }, 5 * 60 * 1000);
129
130 return {
131 response,
132 fromCache: false,
133 timestamp: Date.now()
134 };
135
136 } catch (error) {
137 // TODO: Handle fetch errors
138 throw new Error(`PAI fetch failed: ${error.message}`);
139 }
140 }
141
142 // Helper methods (TODO: Implement these)
143 generateStreamId() {
144 return 'stream_' + Math.random().toString(36).substr(2, 9);
145 }
146
147 generateCacheKey(input, options) {
148 return btoa(JSON.stringify({ input, options }));
149 }
150
151 async simulateTypingDelay() {
152 // TODO: Simulate realistic typing speed (30-60 WPM)
153 const baseDelay = 100; // milliseconds
154 const randomJitter = Math.random() * 50;
155 await this.delay(baseDelay + randomJitter);
156 }
157
158 async delay(ms) {
159 return new Promise(resolve => setTimeout(resolve, ms));
160 }
161
162 async fetchPAIResponse(userInput, options) {
163 // TODO: Simulate API call with realistic delay
164 const processingTime = Math.random() * 2000 + 500; // 500-2500ms
165 await this.delay(processingTime);
166
167 // TODO: Simulate occasional failures
168 if (Math.random() < 0.1) { // 10% failure rate
169 throw new Error('PAI service temporarily unavailable');
170 }
171
172 // TODO: Generate contextual response
173 const responses = [
174 `Based on your input "${userInput}", I understand you're looking for assistance with PAI development.`,
175 `That's an interesting question about "${userInput}". Let me provide a comprehensive response.`,
176 `I can help you with "${userInput}". Here's what I think would be most valuable.`,
177 `Great question about "${userInput}"! This touches on several important PAI concepts.`
178 ];
179
180 return responses[Math.floor(Math.random() * responses.length)];
181 }
182}
183
184// TODO: Implement the User Interface Controller
185class PAIStreamingUI {
186 constructor(paiEngine) {
187 this.paiEngine = paiEngine;
188 this.activeStreams = new Set();
189 this.setupEventListeners();
190 }
191
192 setupEventListeners() {
193 // TODO: Set up input handlers
194 const inputElement = document.getElementById('user-input');
195 const sendButton = document.getElementById('send-button');
196
197 sendButton?.addEventListener('click', () => this.handleUserInput());
198 inputElement?.addEventListener('keypress', (e) => {
199 if (e.key === 'Enter' && !e.shiftKey) {
200 e.preventDefault();
201 this.handleUserInput();
202 }
203 });
204 }
205
206 async handleUserInput() {
207 const inputElement = document.getElementById('user-input');
208 const userInput = inputElement?.value.trim();
209
210 if (!userInput) return;
211
212 // TODO: Clear input and show typing indicator
213 inputElement.value = '';
214 this.showTypingIndicator();
215
216 try {
217 // TODO: Create response container
218 const responseContainer = this.createResponseContainer();
219
220 // TODO: Stream the response
221 const responseStream = this.paiEngine.streamResponse(userInput);
222
223 for await (const chunk of responseStream) {
224 this.handleStreamChunk(chunk, responseContainer);
225 }
226
227 } catch (error) {
228 this.showError(error.message);
229 } finally {
230 this.hideTypingIndicator();
231 }
232 }
233
234 handleStreamChunk(chunk, container) {
235 switch (chunk.type) {
236 case 'start':
237 // TODO: Initialize response display
238 container.innerHTML = '<span class="cursor">โ–Š</span>';
239 break;
240
241 case 'chunk':
242 // TODO: Append new content
243 const existingText = container.textContent.replace('โ–Š', '');
244 container.innerHTML = existingText + chunk.content + '<span class="cursor">โ–Š</span>';
245
246 // TODO: Auto-scroll to bottom
247 container.scrollIntoView({ behavior: 'smooth', block: 'end' });
248 break;
249
250 case 'complete':
251 // TODO: Remove cursor and finalize
252 container.innerHTML = chunk.fullResponse;
253 container.classList.add('complete');
254 break;
255
256 case 'error':
257 // TODO: Show error state
258 container.innerHTML = `<span class="error">โŒ ${chunk.error}</span>`;
259 break;
260 }
261 }
262
263 // TODO: Implement UI helper methods
264 createResponseContainer() {
265 const container = document.createElement('div');
266 container.className = 'pai-response streaming';
267 document.getElementById('chat-container')?.appendChild(container);
268 return container;
269 }
270
271 showTypingIndicator() {
272 // TODO: Show "PAI is thinking..." indicator
273 const indicator = document.getElementById('typing-indicator');
274 if (indicator) {
275 indicator.style.display = 'block';
276 indicator.innerHTML = '๐Ÿค” PAI is thinking...';
277 }
278 }
279
280 hideTypingIndicator() {
281 const indicator = document.getElementById('typing-indicator');
282 if (indicator) {
283 indicator.style.display = 'none';
284 }
285 }
286
287 showError(message) {
288 const errorContainer = document.createElement('div');
289 errorContainer.className = 'error-message';
290 errorContainer.innerHTML = `โŒ Error: ${message}`;
291 document.getElementById('chat-container')?.appendChild(errorContainer);
292 }
293}
294
295// TODO: Test your implementation
296async function testStreamingPAI() {
297 const paiEngine = new StreamingPAIEngine('https://api.example.com/pai');
298 const ui = new PAIStreamingUI(paiEngine);
299
300 // Test 1: Single streaming response
301 console.log('๐Ÿงช Test 1: Single streaming response');
302 const singleStream = paiEngine.streamResponse('How does async JavaScript work?');
303
304 for await (const chunk of singleStream) {
305 console.log('Chunk:', chunk);
306 }
307
308 // Test 2: Multiple concurrent inputs
309 console.log('\n๐Ÿงช Test 2: Multiple concurrent inputs');
310 const multipleInputs = [
311 { text: 'Explain duck typing', options: {}, timestamp: Date.now() },
312 { text: 'What is async/await?', options: {}, timestamp: Date.now() },
313 { text: 'How do promises work?', options: {}, timestamp: Date.now() }
314 ];
315
316 const results = await paiEngine.processMultipleInputs(multipleInputs);
317 console.log('Batch results:', results);
318
319 // Test 3: Caching behavior
320 console.log('\n๐Ÿงช Test 3: Caching behavior');
321 const input = 'What is PAI development?';
322
323 const first = await paiEngine.getCachedOrFetchResponse(input);
324 console.log('First call:', first);
325
326 const second = await paiEngine.getCachedOrFetchResponse(input);
327 console.log('Second call (should be cached):', second);
328}
329
330// Run tests if in Node.js environment
331if (typeof window === 'undefined') {
332 testStreamingPAI().catch(console.error);
333}

Your Implementation

Fill in the missing parts marked with // TODO: and ________. Focus on:

  1. Proper async/await usage for streaming responses
  2. Error handling that doesn't break the user experience
  3. Promise.allSettled() for resilient concurrent processing
  4. Efficient caching with automatic cleanup
  5. Realistic simulation of AI service behavior
๐Ÿ’ก Hint for Exercise 1

Key Patterns to Implement:

  1. Generator Functions: Use async function* for streaming
  2. Promise.allSettled(): For resilient batch processing
  3. Map() for tracking: Keep track of active streams and cache
  4. setTimeout for cleanup: Automatic cache eviction
  5. Try/catch/finally: Proper error boundaries

Example Pattern:

1try {
2 const results = await Promise.allSettled(promises);
3 // Process results regardless of individual failures
4} catch (error) {
5 // Handle overall system errors
6} finally {
7 // Always cleanup resources
8}

๐ŸŒ Exercise 2: Multi-Modal Input Processing

Challenge: Handle Voice, Text, and Gesture Inputs Concurrently

Build a system that can process multiple types of user input simultaneously without blocking.

Code Template

1// Multi-Modal PAI Input Processor
2class MultiModalPAIProcessor {
3 constructor() {
4 this.inputProcessors = {
5 voice: new VoiceInputProcessor(),
6 text: new TextInputProcessor(),
7 gesture: new GestureInputProcessor(),
8 neural: new NeuralInputProcessor()
9 };
10
11 this.eventEmitter = new EventTarget();
12 this.processingQueue = [];
13 this.isProcessing = false;
14 }
15
16 // TODO: Implement concurrent input processing
17 async processInput(inputData, modality) {
18 console.log(`๐Ÿ“ฅ Processing ${modality} input:`, inputData.preview);
19
20 try {
21 // TODO: Get the appropriate processor
22 const processor = this.inputProcessors[modality];
23
24 if (!processor) {
25 throw new Error(`No processor found for modality: ${modality}`);
26 }
27
28 // TODO: Process input with timeout protection
29 const timeoutPromise = new Promise((_, reject) => {
30 setTimeout(() => reject(new Error('Input processing timeout')), 10000);
31 });
32
33 const processingPromise = processor.processInput(inputData);
34
35 // TODO: Race between processing and timeout
36 const result = await Promise.race([processingPromise, timeoutPromise]);
37
38 // TODO: Emit success event
39 this.eventEmitter.dispatchEvent(new CustomEvent('inputProcessed', {
40 detail: {
41 modality,
42 inputData,
43 result,
44 timestamp: Date.now()
45 }
46 }));
47
48 return result;
49
50 } catch (error) {
51 // TODO: Emit error event
52 this.eventEmitter.dispatchEvent(new CustomEvent('inputError', {
53 detail: {
54 modality,
55 inputData,
56 error: error.message,
57 timestamp: Date.now()
58 }
59 }));
60
61 throw error;
62 }
63 }
64
65 // TODO: Implement smart input fusion
66 async fuseMultiModalInputs(inputs) {
67 console.log(`๐Ÿ”„ Fusing ${inputs.length} multi-modal inputs`);
68
69 // TODO: Process all inputs concurrently
70 const processingPromises = inputs.map(async (input) => {
71 try {
72 const result = await this.processInput(input.data, input.modality);
73 return {
74 modality: input.modality,
75 success: true,
76 result,
77 confidence: result.confidence || 0.5,
78 timestamp: Date.now()
79 };
80 } catch (error) {
81 return {
82 modality: input.modality,
83 success: false,
84 error: error.message,
85 confidence: 0,
86 timestamp: Date.now()
87 };
88 }
89 });
90
91 // TODO: Wait for all to complete (don't fail fast)
92 const results = await Promise.allSettled(processingPromises);
93
94 // TODO: Implement fusion algorithm
95 const successfulResults = results
96 .filter(r => r.status === 'fulfilled' && r.value.success)
97 .map(r => r.value);
98
99 if (successfulResults.length === 0) {
100 throw new Error('All input modalities failed to process');
101 }
102
103 // TODO: Weighted fusion based on confidence scores
104 const fusedResult = this.weightedFusion(successfulResults);
105
106 return {
107 fusedResult,
108 individualResults: results,
109 processingTime: Date.now() - inputs[0].timestamp
110 };
111 }
112
113 // TODO: Implement weighted fusion algorithm
114 weightedFusion(results) {
115 // TODO: Calculate total confidence
116 const totalConfidence = results.reduce((sum, r) => sum + r.confidence, 0);
117
118 if (totalConfidence === 0) {
119 // TODO: Fallback to simple average
120 return {
121 intent: 'unclear',
122 confidence: 0,
123 text: 'Could not determine user intent',
124 modalities: results.map(r => r.modality)
125 };
126 }
127
128 // TODO: Weighted average of results
129 let fusedText = '';
130 let fusedIntent = '';
131 let maxConfidence = 0;
132
133 results.forEach(result => {
134 const weight = result.confidence / totalConfidence;
135
136 // TODO: Choose highest confidence intent
137 if (result.confidence > maxConfidence) {
138 maxConfidence = result.confidence;
139 fusedIntent = result.result.intent;
140 }
141
142 // TODO: Concatenate text with weights
143 if (result.result.text) {
144 fusedText += `[${result.modality}:${(weight * 100).toFixed(1)}%] ${result.result.text} `;
145 }
146 });
147
148 return {
149 intent: fusedIntent,
150 confidence: maxConfidence,
151 text: fusedText.trim(),
152 modalities: results.map(r => r.modality),
153 fusionWeights: results.map(r => ({
154 modality: r.modality,
155 weight: r.confidence / totalConfidence
156 }))
157 };
158 }
159}
160
161// TODO: Implement individual input processors
162class VoiceInputProcessor {
163 async processInput(audioData) {
164 // TODO: Simulate voice recognition processing
165 await this.delay(Math.random() * 1500 + 500); // 500-2000ms
166
167 // TODO: Simulate occasional recognition failures
168 if (Math.random() < 0.15) { // 15% failure rate
169 throw new Error('Voice recognition failed - unclear audio');
170 }
171
172 // TODO: Return processed voice data
173 return {
174 text: audioData.transcript || 'Simulated voice transcription',
175 intent: this.detectIntent(audioData.transcript),
176 confidence: Math.random() * 0.4 + 0.6, // 0.6-1.0
177 language: audioData.language || 'en',
178 processingTime: Date.now() - audioData.timestamp
179 };
180 }
181
182 detectIntent(text) {
183 // TODO: Simple intent detection
184 const intents = {
185 'hello': 'greeting',
186 'help': 'assistance',
187 'what': 'question',
188 'how': 'tutorial',
189 'show': 'demonstration',
190 'explain': 'explanation'
191 };
192
193 for (const [keyword, intent] of Object.entries(intents)) {
194 if (text?.toLowerCase().includes(keyword)) {
195 return intent;
196 }
197 }
198
199 return 'general';
200 }
201
202 delay(ms) {
203 return new Promise(resolve => setTimeout(resolve, ms));
204 }
205}
206
207class TextInputProcessor {
208 async processInput(textData) {
209 // TODO: Simulate text processing (faster than voice)
210 await this.delay(Math.random() * 300 + 100); // 100-400ms
211
212 return {
213 text: textData.content,
214 intent: this.detectIntent(textData.content),
215 confidence: 0.9, // Text is generally more reliable
216 sentiment: this.analyzeSentiment(textData.content),
217 processingTime: Date.now() - textData.timestamp
218 };
219 }
220
221 detectIntent(text) {
222 // TODO: More sophisticated text intent detection
223 if (text.includes('?')) return 'question';
224 if (text.includes('please') || text.includes('can you')) return 'request';
225 if (text.includes('thank')) return 'gratitude';
226 if (text.includes('hello') || text.includes('hi')) return 'greeting';
227 return 'statement';
228 }
229
230 analyzeSentiment(text) {
231 // TODO: Simple sentiment analysis
232 const positiveWords = ['good', 'great', 'awesome', 'excellent', 'amazing'];
233 const negativeWords = ['bad', 'terrible', 'awful', 'horrible', 'disappointing'];
234
235 const positive = positiveWords.some(word => text.toLowerCase().includes(word));
236 const negative = negativeWords.some(word => text.toLowerCase().includes(word));
237
238 if (positive && !negative) return 'positive';
239 if (negative && !positive) return 'negative';
240 return 'neutral';
241 }
242
243 delay(ms) {
244 return new Promise(resolve => setTimeout(resolve, ms));
245 }
246}
247
248class GestureInputProcessor {
249 async processInput(gestureData) {
250 // TODO: Simulate gesture recognition processing
251 await this.delay(Math.random() * 1000 + 800); // 800-1800ms
252
253 // TODO: Higher failure rate for gestures
254 if (Math.random() < 0.25) { // 25% failure rate
255 throw new Error('Gesture not recognized');
256 }
257
258 return {
259 text: `Gesture: ${gestureData.type}`,
260 intent: this.mapGestureToIntent(gestureData.type),
261 confidence: Math.random() * 0.3 + 0.4, // 0.4-0.7
262 gestureType: gestureData.type,
263 processingTime: Date.now() - gestureData.timestamp
264 };
265 }
266
267 mapGestureToIntent(gestureType) {
268 const gestureMap = {
269 'wave': 'greeting',
270 'point': 'selection',
271 'thumbs_up': 'approval',
272 'thumbs_down': 'disapproval',
273 'swipe_left': 'navigation',
274 'swipe_right': 'navigation',
275 'tap': 'interaction'
276 };
277
278 return gestureMap[gestureType] || 'unknown_gesture';
279 }
280
281 delay(ms) {
282 return new Promise(resolve => setTimeout(resolve, ms));
283 }
284}
285
286// TODO: Implement the main test function
287async function testMultiModalProcessing() {
288 const processor = new MultiModalPAIProcessor();
289
290 // TODO: Set up event listeners
291 processor.eventEmitter.addEventListener('inputProcessed', (event) => {
292 console.log('โœ… Input processed:', event.detail);
293 });
294
295 processor.eventEmitter.addEventListener('inputError', (event) => {
296 console.log('โŒ Input error:', event.detail);
297 });
298
299 // Test 1: Single modality processing
300 console.log('๐Ÿงช Test 1: Single modality processing');
301
302 try {
303 const voiceResult = await processor.processInput({
304 transcript: 'Hello, can you help me?',
305 timestamp: Date.now(),
306 preview: 'Hello, can you help me?'
307 }, 'voice');
308
309 console.log('Voice result:', voiceResult);
310 } catch (error) {
311 console.log('Voice processing failed:', error.message);
312 }
313
314 // Test 2: Multi-modal fusion
315 console.log('\n๐Ÿงช Test 2: Multi-modal fusion');
316
317 const multiModalInputs = [
318 {
319 modality: 'voice',
320 data: {
321 transcript: 'Show me the weather',
322 timestamp: Date.now(),
323 preview: 'Show me the weather'
324 }
325 },
326 {
327 modality: 'text',
328 data: {
329 content: 'What is the weather like today?',
330 timestamp: Date.now(),
331 preview: 'What is the weather like today?'
332 }
333 },
334 {
335 modality: 'gesture',
336 data: {
337 type: 'point',
338 timestamp: Date.now(),
339 preview: 'pointing gesture'
340 }
341 }
342 ];
343
344 try {
345 const fusionResult = await processor.fuseMultiModalInputs(multiModalInputs);
346 console.log('Fusion result:', fusionResult);
347 } catch (error) {
348 console.log('Multi-modal fusion failed:', error.message);
349 }
350}
351
352// Run test
353testMultiModalProcessing().catch(console.error);

๐ŸŽจ Whiteboard Exercise: Async PAI Architecture Design

Challenge: Design Your Async PAI System Architecture

Instructions:

  1. Open the whiteboard tool
  2. Design an async PAI system showing:
    • Multiple input sources (voice, text, gesture, neural)
    • Async processing pipelines
    • Error handling boundaries
    • Caching layers
    • Real-time UI updates

Consider These Questions:

  • How do you handle partial failures in multi-modal input?
  • Where should caching be implemented for best performance?
  • How do you prevent race conditions in concurrent processing?
  • What's your strategy for handling network timeouts?

Bonus: Add timing diagrams showing async operation flows.


๐Ÿ“Š Performance Challenge: Optimizing Concurrent Operations

Challenge: Benchmark and Optimize Your Async Code

Test different approaches to concurrent processing:

1// Performance Testing Suite
2class AsyncPerformanceTester {
3 constructor() {
4 this.testResults = [];
5 }
6
7 async benchmarkApproach(name, testFunction, iterations = 100) {
8 console.log(`๐Ÿƒโ€โ™‚๏ธ Benchmarking: ${name}`);
9
10 const startTime = performance.now();
11 const promises = [];
12
13 for (let i = 0; i < iterations; i++) {
14 promises.push(testFunction());
15 }
16
17 // TODO: Choose the right Promise method for your test
18 const results = await Promise.all(promises);
19
20 const endTime = performance.now();
21 const duration = endTime - startTime;
22
23 const result = {
24 name,
25 duration: duration.toFixed(2),
26 throughput: (iterations / (duration / 1000)).toFixed(2),
27 successRate: this.calculateSuccessRate(results),
28 timestamp: Date.now()
29 };
30
31 this.testResults.push(result);
32 console.log(`โœ… ${name}: ${result.duration}ms, ${result.throughput} ops/sec`);
33
34 return result;
35 }
36
37 // TODO: Test different concurrency patterns
38 async runPerformanceTests() {
39 // Test 1: Promise.all (fail-fast)
40 await this.benchmarkApproach('Promise.all', async () => {
41 const promises = [
42 this.simulateAPICall(100),
43 this.simulateAPICall(150),
44 this.simulateAPICall(200)
45 ];
46 return Promise.all(promises);
47 });
48
49 // Test 2: Promise.allSettled (resilient)
50 await this.benchmarkApproach('Promise.allSettled', async () => {
51 const promises = [
52 this.simulateAPICall(100),
53 this.simulateAPICall(150),
54 this.simulateAPICall(200)
55 ];
56 return Promise.allSettled(promises);
57 });
58
59 // Test 3: Sequential processing
60 await this.benchmarkApproach('Sequential', async () => {
61 await this.simulateAPICall(100);
62 await this.simulateAPICall(150);
63 await this.simulateAPICall(200);
64 });
65
66 // TODO: Analyze and display results
67 this.displayResults();
68 }
69
70 async simulateAPICall(delay) {
71 await new Promise(resolve => setTimeout(resolve, delay));
72
73 // TODO: Simulate random failures
74 if (Math.random() < 0.1) {
75 throw new Error('Simulated API failure');
76 }
77
78 return { success: true, delay };
79 }
80
81 calculateSuccessRate(results) {
82 if (!Array.isArray(results)) return 100;
83
84 const successful = results.filter(r =>
85 r.status === 'fulfilled' || (r.status !== 'rejected' && !r.error)
86 ).length;
87
88 return ((successful / results.length) * 100).toFixed(1);
89 }
90
91 displayResults() {
92 console.log('\n๐Ÿ“Š Performance Test Results:');
93 console.table(this.testResults);
94
95 // TODO: Find the fastest approach
96 const fastest = this.testResults.reduce((prev, current) =>
97 parseFloat(prev.duration) < parseFloat(current.duration) ? prev : current
98 );
99
100 console.log(`๐Ÿ† Fastest approach: ${fastest.name} (${fastest.duration}ms)`);
101 }
102}
103
104// TODO: Run performance tests
105const tester = new AsyncPerformanceTester();
106tester.runPerformanceTests().catch(console.error);

Analysis Questions:

  1. When should you use Promise.all() vs Promise.allSettled()?

    • Promise.all(): ____________
    • Promise.allSettled(): ____________
  2. How does concurrent processing compare to sequential?

    • Performance difference: ____________
    • Error handling difference: ____________
  3. What patterns work best for PAI systems?

    • Real-time responses: ____________
    • Batch processing: ____________
    • Error-critical operations: ____________

๐Ÿ† Advanced Challenge: Error-Resilient PAI Network

Challenge: Build a Self-Healing PAI Communication System

Create a system that gracefully handles network issues, service outages, and partial failures:

1class ResilientPAINetwork {
2 constructor(endpoints) {
3 this.endpoints = endpoints;
4 this.circuitBreakers = new Map();
5 this.retryStrategies = new Map();
6 this.healthStatus = new Map();
7
8 // TODO: Initialize circuit breakers for each endpoint
9 endpoints.forEach(endpoint => {
10 this.circuitBreakers.set(endpoint, new CircuitBreaker(endpoint));
11 this.healthStatus.set(endpoint, 'unknown');
12 });
13
14 // TODO: Start health monitoring
15 this.startHealthMonitoring();
16 }
17
18 // TODO: Implement circuit breaker pattern
19 async callWithCircuitBreaker(endpoint, operation) {
20 const circuitBreaker = this.circuitBreakers.get(endpoint);
21
22 if (circuitBreaker.isOpen()) {
23 throw new Error(`Circuit breaker OPEN for ${endpoint}`);
24 }
25
26 try {
27 const result = await operation();
28 circuitBreaker.recordSuccess();
29 return result;
30 } catch (error) {
31 circuitBreaker.recordFailure();
32 throw error;
33 }
34 }
35
36 // TODO: Implement smart retry with exponential backoff
37 async retryWithBackoff(operation, maxRetries = 3) {
38 for (let attempt = 1; attempt <= maxRetries; attempt++) {
39 try {
40 return await operation();
41 } catch (error) {
42 if (attempt === maxRetries) {
43 throw error;
44 }
45
46 // TODO: Exponential backoff with jitter
47 const baseDelay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
48 const jitter = Math.random() * 1000; // 0-1s jitter
49 const delay = baseDelay + jitter;
50
51 console.log(`โณ Retry attempt ${attempt}/${maxRetries} in ${delay}ms`);
52 await new Promise(resolve => setTimeout(resolve, delay));
53 }
54 }
55 }
56
57 // TODO: Implement health monitoring
58 startHealthMonitoring() {
59 setInterval(async () => {
60 for (const endpoint of this.endpoints) {
61 try {
62 const startTime = Date.now();
63 await this.healthCheck(endpoint);
64 const responseTime = Date.now() - startTime;
65
66 this.healthStatus.set(endpoint, {
67 status: 'healthy',
68 responseTime,
69 lastCheck: Date.now()
70 });
71
72 } catch (error) {
73 this.healthStatus.set(endpoint, {
74 status: 'unhealthy',
75 error: error.message,
76 lastCheck: Date.now()
77 });
78 }
79 }
80 }, 30000); // Check every 30 seconds
81 }
82
83 async healthCheck(endpoint) {
84 // TODO: Implement health check
85 const timeout = new Promise((_, reject) =>
86 setTimeout(() => reject(new Error('Health check timeout')), 5000)
87 );
88
89 const healthRequest = fetch(`${endpoint}/health`);
90
91 return Promise.race([healthRequest, timeout]);
92 }
93
94 // TODO: Get healthy endpoints for load balancing
95 getHealthyEndpoints() {
96 return this.endpoints.filter(endpoint => {
97 const health = this.healthStatus.get(endpoint);
98 return health?.status === 'healthy';
99 });
100 }
101}
102
103// TODO: Circuit Breaker Implementation
104class CircuitBreaker {
105 constructor(endpoint, threshold = 5, timeout = 60000) {
106 this.endpoint = endpoint;
107 this.threshold = threshold;
108 this.timeout = timeout;
109 this.failureCount = 0;
110 this.lastFailureTime = null;
111 this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
112 }
113
114 isOpen() {
115 if (this.state === 'OPEN') {
116 // TODO: Check if timeout period has passed
117 if (Date.now() - this.lastFailureTime > this.timeout) {
118 this.state = 'HALF_OPEN';
119 return false;
120 }
121 return true;
122 }
123 return false;
124 }
125
126 recordSuccess() {
127 this.failureCount = 0;
128 this.state = 'CLOSED';
129 }
130
131 recordFailure() {
132 this.failureCount++;
133 this.lastFailureTime = Date.now();
134
135 if (this.failureCount >= this.threshold) {
136 this.state = 'OPEN';
137 console.log(`๐Ÿ”ด Circuit breaker OPEN for ${this.endpoint}`);
138 }
139 }
140}

๐Ÿ“š Solution Review & Key Takeaways

Exercise Solutions

๐Ÿ”“ Click to View Key Solutions

Exercise 1 - Key Implementations:

1// Promise.allSettled for resilient processing
2const results = await Promise.allSettled(promises);
3
4// Proper async generator
5async *streamResponse(userInput, options = {}) {
6 // Implementation with proper error handling
7}
8
9// Efficient caching with cleanup
10setTimeout(() => {
11 this.responseCache.delete(cacheKey);
12}, 5 * 60 * 1000);

Exercise 2 - Multi-Modal Fusion:

1// Resilient concurrent processing
2const results = await Promise.allSettled(processingPromises);
3
4// Weighted fusion algorithm
5const weightedFusion = (results) => {
6 const totalConfidence = results.reduce((sum, r) => sum + r.confidence, 0);
7 // Implementation...
8};

Key Takeaways

โœ… Use Promise.allSettled() for operations where partial success is acceptable
โœ… Implement circuit breakers to prevent cascading failures
โœ… Add exponential backoff for reliable retry mechanisms
โœ… Stream responses for better user experience
โœ… Cache intelligently with automatic cleanup
โœ… Monitor health of all services continuously
โœ… Handle timeouts gracefully to prevent hanging operations

Performance Insights

  • Concurrent processing can be 3-5x faster than sequential
  • Promise.allSettled() provides better resilience with only 10-20% performance overhead
  • Caching can reduce response times by 80-90%
  • Circuit breakers prevent up to 95% of unnecessary failed requests

Next Steps

  1. Implement real API integration using these patterns
  2. Add metrics collection for monitoring performance
  3. Build error recovery mechanisms for critical failures
  4. Explore WebRTC for real-time voice processing
  5. Study the next exercise: "Real-time PAI WebSocket Communication"

Congratulations! You've mastered async JavaScript patterns for responsive PAI systems. Your AI can now handle multiple inputs gracefully and recover from failures automatically. ๐ŸŽ‰

๐ŸŽฏExercise Progress

Overall Progress0/10 exercises
Current ExerciseStep 1/5
โฑ๏ธTime Spent
0 minutes

๐Ÿ“šExercise Details

Language:JavaScript
Difficulty Score:7/10
Estimated Time:75-90 minutes
Series:Part 2

Learning Objectives:

  • ๐ŸŽฏImplement async/await patterns for PAI response handling
  • ๐ŸŽฏCreate event-driven PAI communication systems
  • ๐ŸŽฏBuild reactive user interface updates
  • ๐ŸŽฏHandle error scenarios gracefully in async PAI operations
  • ๐ŸŽฏOptimize performance with concurrent promise execution
Required
๐Ÿ’ก

Whiteboard Recommended!

This exercise works best with visual diagrams and notes. Click to open your whiteboard workspace.