Lesson 17-Deno Basic Programming

Basic Syntax and APIs

Module Imports and Exports

URL Import Mechanism
Deno uses a URL-based ES module system, requiring all dependencies to explicitly declare their sources. This design offers transparency and portability advantages.

// Import standard library module (with version)
import { serve } from "https://deno.land/std@0.140.0/http/server.ts";

// Import third-party module (CDN-hosted)
import lodash from "https://cdn.skypack.dev/lodash@4.17.21";

// Import local module
import { helper } from "./utils.ts";

Dynamic Import Implementation
Dynamic imports allow on-demand module loading at runtime, returning a Promise:

async function loadLogger(level: string) {
  let logger;
  if (level === "debug") {
    logger = await import("https://example.com/debug_logger.ts");
  } else {
    logger = await import("https://example.com/production_logger.ts");
  }
  return logger.getLogger();
}

// Usage example
const logger = await loadLogger("debug");
logger.log("Debug message");

Module Export Specification
Deno supports all ES module export methods:

// Named export
export function add(a: number, b: number) {
  return a + b;
}

// Default export
const PI = 3.14159;
export default PI;

// Mixed export
export { add };
export { PI as default };

File System Operations

Basic File Reading and Writing
Deno’s file operations require explicit permissions, and all APIs return Promises:

// Asynchronous file reading
const text = await Deno.readTextFile("input.txt");
console.log(text);

// Synchronous reading (not recommended)
const textSync = Deno.readTextFileSync("input.txt");

// File writing (requires --allow-write)
await Deno.writeTextFile("output.txt", "Hello Deno!");

// Binary file operations
const data = new Uint8Array([65, 66, 67]); // ASCII: ABC
await Deno.writeFile("binary.bin", data);

Advanced File System Operations

// Copy file (requires --allow-read --allow-write)
await Deno.copyFile("source.txt", "dest.txt");

// File status check
const fileInfo = await Deno.stat("file.txt");
console.log(fileInfo.size, fileInfo.mtime);

// Directory operations
// Create directory
await Deno.mkdir("new_dir", { recursive: true });

// Read directory
const entries = await Deno.readDir(".");
for await (const entry of entries) {
  console.log(entry.name, entry.isFile);
}

Network Programming Basics

HTTP Server Implementation

import { serve } from "https://deno.land/std@0.140.0/http/server.ts";

const server = serve({ port: 8000 });
console.log("Server running on http://localhost:8000");

for await (const req of server) {
  // Handle request
  req.respond({
    status: 200,
    body: "Hello from Deno server!"
  });
}

HTTP Client Requests

// GET request
const resp = await fetch("https://jsonplaceholder.typicode.com/posts/1");
const data = await resp.json();
console.log(data);

// POST request
const postData = new URLSearchParams({
  title: "foo",
  body: "bar",
  userId: "1"
});

const postResp = await fetch("https://jsonplaceholder.typicode.com/posts", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: postData
});

console.log(await postResp.json());

WebSocket Communication

// Server
import { serve } from "https://deno.land/std@0.140.0/http/server.ts";

const server = serve({ port: 8080 });
console.log("WebSocket server running on ws://localhost:8080");

for await (const req of server) {
  const { conn, r: bufReader, w: bufWriter, headers } = req;
  const socket = await Deno.upgradeWebSocket(conn, bufReader, bufWriter, headers);

  socket.onmessage = (e) => {
    console.log("Received:", e.data);
    socket.send(`Echo: ${e.data}`);
  };
}

// Client
const socket = new WebSocket("ws://localhost:8080");
socket.onmessage = (e) => console.log("Server:", e.data);
socket.send("Hello WebSocket!");

Timers and Event Loop

Basic Timers

// setTimeout
const timeoutId = setTimeout(() => {
  console.log("Timeout executed");
}, 1000);

// clearTimeout
clearTimeout(timeoutId);

// setInterval
const intervalId = setInterval(() => {
  console.log("Interval tick");
}, 2000);

// clearInterval
clearInterval(intervalId);

Event Loop Demonstration

console.log("Script start");

setTimeout(() => {
  console.log("setTimeout");
}, 0);

Promise.resolve().then(() => {
  console.log("Promise 1");
}).then(() => {
  console.log("Promise 2");
});

queueMicrotask(() => {
  console.log("queueMicrotask");
});

console.log("Script end");

/*
Output order:
Script start
Script end
Promise 1
queueMicrotask
Promise 2
setTimeout
*/

Processes and Subprocesses

Executing External Commands

// Execute simple command
const { code } = await Deno.run({
  cmd: ["echo", "Hello from subprocess"]
}).status();

console.log(`Exit code: ${code}`);

// Capture output
const process = Deno.run({
  cmd: ["ls", "-la"],
  stdout: "piped",
  stderr: "piped"
});

const { code: exitCode } = await process.status();
if (exitCode === 0) {
  const output = new TextDecoder().decode(await process.output());
  console.log(output);
} else {
  const error = new TextDecoder().decode(await process.stderrOutput());
  console.error(error);
}

Advanced Process Control

// Process with environment variables
const envProcess = Deno.run({
  cmd: ["printenv"],
  env: {
    CUSTOM_VAR: "DenoValue"
  },
  stdout: "piped"
});

// Process with working directory
const cwdProcess = Deno.run({
  cmd: ["pwd"],
  cwd: "/tmp",
  stdout: "piped"
});

Permissions and Security

Configuring the Permission System

Basic Permission Flags

# File system permissions
deno run --allow-read=file.txt script.ts
deno run --allow-write=/path/to/dir script.ts

# Network permissions
deno run --allow-net=api.example.com script.ts

# Environment variable permissions
deno run --allow-env=HOME,PATH script.ts

# Combined permissions
deno run --allow-read --allow-net script.ts

Permission Scope Control

# Allow network access for specific port
deno run --allow-net=127.0.0.1:8000 script.ts

# Allow all network but restrict file system
deno run --allow-net --allow-read=/tmp script.ts

Sandbox Mechanism Implementation

Security Isolation Features

  • No permissions by default.
  • All system access must be explicitly declared.
  • No global object pollution (Node.js’s __dirname, etc., are absent).
  • Module cache isolation.

Underlying Implementation

  1. Permission checks occur before system calls.
  2. Each permission operation has a corresponding checkpoint.
  3. Permission violations trigger immediate errors.
  4. No persistent state storage.

Security Best Practices

Applying the Principle of Least Privilege

# Bad: Over-privileged
deno run --allow-all script.ts

# Good: Precise permissions
deno run --allow-read=data.json --allow-net=api.example.com script.ts

Permission Usage Checklist

  1. Does it need file system access? Specify exact paths.
  2. Does it need network access? Restrict to specific domains/ports.
  3. Does it need environment variables? List required variables explicitly.
  4. Does it need subprocess execution? Evaluate necessity.

Dynamic Permission Requests

Runtime Permission Checks

// Check current permission status
const status = await Deno.permissions.query({ name: "read" });
if (status.state === "granted") {
  // Permission granted
} else if (status.state === "prompt") {
  // Can request permission
  const granted = await Deno.permissions.request({ name: "read" });
  if (granted) {
    // User granted permission
  }
}

// Re-request permission
await Deno.permissions.request({ name: "net" });

// Revoke permission
await Deno.permissions.revoke({ name: "env" });

Interactive Permission Example

async function checkPermission(name: string) {
  const status = await Deno.permissions.query({ name });
  if (status.state === "granted") return true;

  console.log(`Requires ${name} permission, please confirm...`);
  const granted = await Deno.permissions.request({ name });
  return granted;
}

if (await checkPermission("read")) {
  const content = await Deno.readTextFile("config.json");
  console.log(content);
}

Security Vulnerabilities and Protections

Common Security Risks

  1. Unsafe dependency imports.
  2. Excessive permission grants.
  3. Unvalidated user input.
  4. Hardcoded sensitive data.

Protection Measures

// Input validation example
function sanitizeInput(input: string) {
  return input.replace(/[<>"'&]/g, "");
}

// Sensitive data handling
const sensitiveData = Deno.env.get("DB_PASSWORD");
if (sensitiveData) {
  // Clear after use
  Deno.env.delete("DB_PASSWORD");
}

// Dependency validation
const deps = await Deno.readTextFile("deps.ts");
if (deps.includes("http://")) {
  throw new Error("Unsafe dependency source");
}

Asynchronous Programming

Promise and async/await Basics

Promise Basic Usage

// Create Promise
function delay(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// Use Promise
delay(1000).then(() => {
  console.log("Executed after 1 second");
});

// Promise chaining
fetch("https://api.example.com/data")
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));

async/await Syntax

// Basic async function
async function fetchData() {
  try {
    const response = await fetch("https://api.example.com/data");
    const data = await response.json();
    return data;
  } catch (error) {
    console.error("Request failed:", error);
    throw error;
  }
}

// Parallel execution
async function parallelTasks() {
  const [user, posts] = await Promise.all([
    fetch("/user").then(res => res.json()),
    fetch("/posts").then(res => res.json())
  ]);
  return { user, posts };
}

Event-Driven Programming

Custom EventEmitter

class EventEmitter {
  private listeners: Map<string, Function[]> = new Map();

  on(event: string, listener: Function) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, []);
    }
    this.listeners.get(event)?.push(listener);
  }

  emit(event: string, ...args: any[]) {
    this.listeners.get(event)?.forEach(listener => {
      listener(...args);
    });
  }

  off(event: string, listener: Function) {
    const listeners = this.listeners.get(event);
    if (listeners) {
      this.listeners.set(
        event,
        listeners.filter(l => l !== listener)
      );
    }
  }
}

// Usage example
const emitter = new EventEmitter();
emitter.on("message", (msg) => {
  console.log("Received message:", msg);
});
emitter.emit("message", "Hello World");

Node.js-Style Events

import { EventEmitter } from "https://deno.land/std@0.140.0/node/events.ts";

const stdEmitter = new EventEmitter();
stdEmitter.on("tick", () => {
  console.log("Tick event");
});
setInterval(() => stdEmitter.emit("tick"), 1000);

Asynchronous I/O Operations

Efficient File Handling

// Stream reading large file
const file = await Deno.open("large_file.txt", { read: true });
const reader = new BufReader(file);

let line: string;
while ((line = await reader.readLine()) !== null) {
  console.log(line);
}

file.close();

// Asynchronous file copy
async function copyFile(src: string, dest: string) {
  const srcFile = await Deno.open(src, { read: true });
  const destFile = await Deno.open(dest, { write: true, create: true });

  await Deno.copy(srcFile, destFile);

  srcFile.close();
  destFile.close();
}

Advanced Network Operations

// HTTP client implementation
async function httpRequest(url: string, method = "GET", body?: string) {
  const resp = await fetch(url, {
    method,
    headers: { "Content-Type": "application/json" },
    body
  });

  if (!resp.ok) {
    throw new Error(`HTTP error: ${resp.status}`);
  }

  return resp.json();
}

// WebSocket message handler
async function handleWebSocket(url: string) {
  const socket = await WebSocket.connect(url);

  socket.onmessage = (e) => {
    console.log("Received message:", e.data);
    socket.send(`Echo: ${e.data}`);
  };

  socket.onclose = () => console.log("Connection closed");
  socket.onerror = (e) => console.error("Connection error:", e);
}

Concurrency Control

Promise Combination Methods

// Parallel execution
async function fetchAll() {
  const [user, posts] = await Promise.all([
    fetch("/user").then(res => res.json()),
    fetch("/posts").then(res => res.json())
  ]);
  return { user, posts };
}

// Race execution
async function fetchFastest() {
  try {
    const response = await Promise.race([
      fetch("/primary-api"),
      fetch("/fallback-api")
    ]);
    return response.json();
  } catch (error) {
    console.error("All requests failed:", error);
  }
}

// Limit concurrency
async function limitedParallel(tasks: (() => Promise<any>)[], limit: number) {
  const results: any[] = [];
  const executing: Promise<any>[] = [];

  for (const task of tasks) {
    const p = task().then(res => {
      results.push(res);
      executing.splice(executing.indexOf(p), 1);
    });

    executing.push(p);
    if (executing.length >= limit) {
      await Promise.race(executing);
    }
  }

  await Promise.all(executing);
  return results;
}

Asynchronous Error Handling

Error Handling Patterns

// Basic try-catch
async function riskyOperation() {
  try {
    const data = await fetch("/unstable-api");
    return await data.json();
  } catch (error) {
    console.error("Operation failed:", error);
    throw error; // Rethrow or return default
  }
}

// Error boundary pattern
async function withErrorBoundary<T>(fn: () => Promise<T>, fallback: T) {
  try {
    return await fn();
  } catch (error) {
    console.error("Error boundary caught:", error);
    return fallback;
  }
}

// Global unhandled Promise rejection
Deno.unstable_setExitHandler((status) => {
  if (status !== 0) {
    console.error("Process exited abnormally:", status);
  }
});

Advanced Error Recovery

// Retry mechanism
async function retry<T>(
  fn: () => Promise<T>,
  maxAttempts = 3,
  delayMs = 1000
): Promise<T> {
  let lastError: Error;

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      if (attempt < maxAttempts) {
        await new Promise(res => setTimeout(res, delayMs));
      }
    }
  }

  throw lastError;
}

// Circuit breaker pattern
class CircuitBreaker {
  private state: "CLOSED" | "OPEN" | "HALF_OPEN" = "CLOSED";
  private failureCount = 0;
  private readonly threshold: number;
  private readonly resetTimeout: number;

  constructor(threshold = 3, resetTimeout = 5000) {
    this.threshold = threshold;
    this.resetTimeout = resetTimeout;
  }

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === "OPEN") {
      throw new Error("Circuit breaker open");
    }

    try {
      const result = await fn();
      this.failureCount = 0;
      return result;
    } catch (error) {
      this.failureCount++;
      if (this.failureCount >= this.threshold) {
        this.state = "OPEN";
        setTimeout(() => {
          this.state = "HALF_OPEN";
        }, this.resetTimeout);
      }
      throw error;
    }
  }
}

Asynchronous Programming Models and Deno Runtime Features

In-Depth Analysis of Event Loop Mechanism

Deno’s event loop, built on the V8 engine, differs significantly from Node.js. It consists of six phases:

  1. Timers: Processes setTimeout and setInterval callbacks.
  2. Pending Callbacks: Executes system-level callbacks (e.g., TCP errors).
  3. Idle/Prepare: Internal use.
  4. Poll: Retrieves new I/O events.
  5. Check: setImmediate callbacks (triggered via Deno.run, etc., in Deno).
  6. Close Callbacks: Handles close event callbacks.
// Event loop phase demonstration
console.log("Script start"); // Synchronous code

setTimeout(() => {
  console.log("setTimeout"); // Timers phase
}, 0);

Promise.resolve().then(() => {
  console.log("Promise 1"); // Microtask 1
}).then(() => {
  console.log("Promise 2"); // Microtask 2
});

queueMicrotask(() => {
  console.log("queueMicrotask"); // Microtask 3
});

Deno.run({ cmd: ["echo", "hello"] }); // May trigger check phase callbacks

console.log("Script end"); // Synchronous code

/*
Typical output order:
Script start
Script end
Promise 1
queueMicrotask
Promise 2
setTimeout
(Possibly interspersed with Deno.run-related callbacks)
*/

Microtask Queue Priority

Deno strictly prioritizes microtasks over macrotasks:

// Microtask priority verification
let order = [];
setTimeout(() => order.push("timeout"), 0);
Promise.resolve().then(() => order.push("promise"));
queueMicrotask(() => order.push("microtask"));

setTimeout(() => {
  console.log(order); // Output: ["promise", "microtask", "timeout"]
}, 100);

Deno-Specific Asynchronous APIs

Deno offers asynchronous APIs distinct from Node.js:

// Deno-specific asynchronous file operation
const file = await Deno.open("test.txt", { read: true });
const content = new Uint8Array(1024);
const bytesRead = await file.read(content); // Returns Promise<number>
await file.close();

// Contrast with Node.js callback style
// fs.readFile('test.txt', (err, data) => {...});

Core Asynchronous Programming Patterns

Advanced Promise Usage

Optimized Promise Chaining

// Best practices for chained error handling
function fetchUserData(userId: string) {
  return fetch(`/api/users/${userId}`)
    .then(response => {
      if (!response.ok) {
        throw new Error(`HTTP error ${response.status}`);
      }
      return response.json();
    })
    .then(user => fetch(`/api/users/${user.id}/posts`))
    .then(response => response.json())
    .catch(error => {
      console.error("Fetch failed:", error);
      throw error; // Rethrow to maintain chain
    });
}

// Usage example
fetchUserData("123")
  .then(posts => console.log(posts))
  .catch(error => console.error("Final error:", error));

Promise Composition Patterns

// Promise.allSettled use case
async function batchRequests(urls: string[]) {
  const results = await Promise.allSettled(
    urls.map(url => fetch(url).then(res => res.json()))
  );

  const successful = results
    .filter(result => result.status === "fulfilled")
    .map(result => (result as PromiseFulfilledResult<any>).value);

  const failed = results
    .filter(result => result.status === "rejected")
    .map(result => (result as PromiseRejectedResult).reason);

  return { successful, failed };
}

Asynchronous Generators and Iteration

Paginated Data with Async Generators

async function* paginatedFetcher(url: string) {
  let page = 1;
  while (true) {
    const response = await fetch(`${url}?page=${page}`);
    const data = await response.json();

    if (data.items.length === 0) break;

    yield* data.items;
    page++;
  }
}

// Usage example
(async () => {
  for await (const item of paginatedFetcher("/api/items")) {
    console.log(item);
  }
})();

Async Iterators and for-await-of

class AsyncQueue<T> {
  private queue: T[] = [];
  private resolveNext: ((value: T | PromiseLike<T>) => void) | null = null;

  enqueue(item: T) {
    if (this.resolveNext) {
      const resolve = this.resolveNext;
      this.resolveNext = null;
      resolve(item);
    } else {
      this.queue.push(item);
    }
  }

  async *[Symbol.asyncIterator]() {
    while (true) {
      if (this.queue.length > 0) {
        yield this.queue.shift()!;
      } else {
        await new Promise<T>(resolve => {
          this.resolveNext = resolve;
        });
      }
    }
  }
}

// Usage example
(async () => {
  const queue = new AsyncQueue<number>();
  setTimeout(() => queue.enqueue(1), 100);
  setTimeout(() => queue.enqueue(2), 200);

  for await (const item of queue) {
    console.log(item);
    if (item === 2) break; // Prevent infinite loop
  }
})();

Asynchronous Error Handling Patterns

Error Boundary Pattern

async function withErrorBoundary<T>(
  fn: () => Promise<T>,
  fallback: T,
  errorHandler?: (error: unknown) => void
): Promise<T> {
  try {
    return await fn();
  } catch (error) {
    errorHandler?.(error);
    return fallback;
  }
}

// Usage example
const result = await withErrorBoundary(
  () => fetch("/unstable-api").then(res => res.json()),
  { default: "data" },
  error => console.error("API request failed:", error)
);

Retry Mechanism Implementation

async function retry<T>(
  fn: () => Promise<T>,
  options: {
    maxAttempts?: number;
    delayMs?: number;
    shouldRetry?: (error: unknown) => boolean;
  } = {}
): Promise<T> {
  const {
    maxAttempts = 3,
    delayMs = 1000,
    shouldRetry = () => true
  } = options;

  let lastError: unknown;

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      if (attempt < maxAttempts && shouldRetry(error)) {
        await new Promise(resolve => setTimeout(resolve, delayMs));
      }
    }
  }

  throw lastError;
}

// Usage example
const data = await retry(
  () => fetch("/flaky-api").then(res => res.json()),
  {
    maxAttempts: 5,
    delayMs: 2000,
    shouldRetry: error => !(error instanceof TypeError) // Skip network errors
  }
);

Advanced Concurrency Control Patterns

Promise Concurrency Control

Dynamic Concurrency Pool Implementation

class PromisePool {
  private queue: Array<() => Promise<any>> = [];
  private activeCount = 0;

  constructor(private maxConcurrent: number) {}

  add(task: () => Promise<any>) {
    return new Promise((resolve, reject) => {
      const wrappedTask = async () => {
        try {
          this.activeCount++;
          const result = await task();
          resolve(result);
        } catch (error) {
          reject(error);
        } finally {
          this.activeCount--;
          this.next();
        }
      };

      this.queue.push(wrappedTask);
      this.next();
    });
  }

  private next() {
    if (this.activeCount < this.maxConcurrent && this.queue.length > 0) {
      const task = this.queue.shift()!;
      task();
    }
  }
}

// Usage example
const pool = new PromisePool(3);
const tasks = Array(10).fill(0).map((_, i) => 
  () => fetch(`/api/item/${i}`).then(res => res.json())
);

await Promise.all(tasks.map(task => pool.add(task)));

Priority-Based Concurrency Control

class PriorityPromisePool {
  private highPriorityQueue: Array<() => Promise<any>> = [];
  private lowPriorityQueue: Array<() => Promise<any>> = [];
  private activeCount = 0;

  constructor(private maxConcurrent: number) {}

  addHighPriority(task: () => Promise<any>) {
    return this.addTask(task, this.highPriorityQueue);
  }

  addLowPriority(task: () => Promise<any>) {
    return this.addTask(task, this.lowPriorityQueue);
  }

  private addTask(task: () => Promise<any>, queue: Array<() => Promise<any>>) {
    return new Promise((resolve, reject) => {
      const wrappedTask = async () => {
        try {
          this.activeCount++;
          const result = await task();
          resolve(result);
        } catch (error) {
          reject(error);
        } finally {
          this.activeCount--;
          this.next();
        }
      };

      queue.push(wrappedTask);
      this.next();
    });
  }

  private next() {
    if (this.activeCount < this.maxConcurrent) {
      if (this.highPriorityQueue.length > 0) {
        const task = this.highPriorityQueue.shift()!;
        task();
      } else if (this.lowPriorityQueue.length > 0) {
        const task = this.lowPriorityQueue.shift()!;
        task();
      }
    }
  }
}

Asynchronous Resource Management

Async Resource Pool Pattern

class AsyncResourcePool<T> {
  private available: T[] = [];
  private inUse: Set<T> = new Set();
  private waiters: Array<(resource: T) => void> = [];

  constructor(
    private factory: () => Promise<T>,
    private reset: (resource: T) => Promise<void>,
    private maxSize: number
  ) {}

  async acquire(): Promise<T> {
    if (this.available.length > 0) {
      const resource = this.available.pop()!;
      this.inUse.add(resource);
      return resource;
    }

    if (this.inUse.size < this.maxSize) {
      const resource = await this.factory();
      this.inUse.add(resource);
      return resource;
    }

    return new Promise(resolve => {
      this.waiters.push(resource => {
        this.inUse.add(resource);
        resolve(resource);
      });
    });
  }

  async release(resource: T) {
    if (!this.inUse.has(resource)) {
      throw new Error("Resource not in use");
    }

    this.inUse.delete(resource);
    await this.reset(resource);

    if (this.waiters.length > 0) {
      const waiter = this.waiters.shift()!;
      waiter(resource);
    } else {
      this.available.push(resource);
    }
  }
}

// Usage example
const dbConnectionPool = new AsyncResourcePool(
  async () => {
    console.log("Creating new connection");
    return { id: Math.random() } as any;
  },
  async (conn) => {
    console.log("Resetting connection", conn.id);
  },
  3
);

// Concurrent use of connection pool
await Promise.all(Array(10).fill(0).map(async () => {
  const conn = await dbConnectionPool.acquire();
  try {
    // Use connection
    console.log("Using connection", conn.id);
    await new Promise(resolve => setTimeout(resolve, 1000));
  } finally {
    await dbConnectionPool.release(conn);
  }
}));

Asynchronous Programming Practical Cases

High-Performance Web Crawler Implementation

async function crawl(url: string, options: {
  maxDepth: number;
  concurrency: number;
  delayMs: number;
}) {
  const visited = new Set<string>();
  const queue: Array<{ url: string; depth: number }> = [{ url, depth: 0 }];
  const pool = new PromisePool(options.concurrency);

  while (queue.length > 0) {
    const { url, depth } = queue.shift()!;
    if (depth > options.maxDepth || visited.has(url)) continue;

    visited.add(url);
    await pool.add(async () => {
      try {
        console.log("Crawling:", url);
        const response = await fetch(url);
        const html = await response.text();

        // Parse links (simplified example)
        const links = extractLinks(html);
        for (const link of links) {
          if (!visited.has(link)) {
            queue.push({ url: link, depth: depth + 1 });
          }
        }

        await new Promise(resolve => setTimeout(resolve, options.delayMs));
      } catch (error) {
        console.error("Failed to crawl:", url, error);
      }
    });
  }
}

// Simplified link extraction function
function extractLinks(html: string): string[] {
  // Actual implementation requires HTML parser
  return [];
}

Real-Time Data Processing Pipeline

async function createDataPipeline(
  source: AsyncIterable<any>,
  processors: Array<(data: any) => Promise<any>>,
  sink: (data: any) => Promise<void>
) {
  let currentStream = source;

  // Build processing pipeline
  for (const processor of processors) {
    const nextStream = (async function* () {
      for await (const data of currentStream) {
        yield await processor(data);
      }
    })();
    currentStream = nextStream;
  }

  // Consume final stream
  for await (const data of currentStream) {
    await sink(data);
  }
}

// Usage example
(async () => {
  // Simulated data source
  async function* dataSource() {
    for (let i = 0; i < 10; i++) {
      await new Promise(resolve => setTimeout(resolve, 100));
      yield { id: i, value: Math.random() };
    }
  }

  // Processing functions
  const processors = [
    async (data) => ({ ...data, processed: true }),
    async (data) => ({ ...data, timestamp: Date.now() })
  ];

  // Data sink
  async function sink(data) {
    console.log("Processed data:", data);
  }

  await createDataPipeline(dataSource(), processors, sink);
})();

Performance Optimization and Debugging Techniques

Asynchronous Code Performance Analysis

// Async operation performance measurement decorator
function measureAsync<T extends (...args: any[]) => Promise<any>>(
  fn: T,
  name: string
): (...args: Parameters<T>) => ReturnType<T> {
  return async function(...args: Parameters<T>): Promise<ReturnType<T>> {
    const start = performance.now();
    try {
      const result = await fn(...args);
      const duration = performance.now() - start;
      console.log(`${name} executed in ${duration.toFixed(2)}ms`);
      return result;
    } catch (error) {
      const duration = performance.now() - start;
      console.error(`${name} failed after ${duration.toFixed(2)}ms`, error);
      throw error;
    }
  };
}

// Usage example
const fetchWithMetrics = measureAsync(
  async (url: string) => {
    const response = await fetch(url);
    return response.json();
  },
  "fetchData"
);

await fetchWithMetrics("https://api.example.com/data");

Asynchronous Debugging Techniques

Async Call Stack Tracing

// Enhanced async error stack
async function tracedAsync<T>(fn: () => Promise<T>, context: string) {
  try {
    return await fn();
  } catch (error) {
    if (error instanceof Error) {
      error.stack += `\nAsync Context: ${context}`;
    }
    throw error;
  }
}

// Usage example
await tracedAsync(
  async () => {
    await fetch("/api");
    throw new Error("Test error");
  },
  "API request flow"
);

Async Operation Visualization

// Simple async operation visualization tool
class AsyncVisualizer {
  private tasks: Map<string, { start: number; end?: number }> = new Map();

  start(taskName: string) {
    this.tasks.set(taskName, { start: performance.now() });
  }

  end(taskName: string) {
    const task = this.tasks.get(taskName);
    if (task) {
      task.end = performance.now();
    }
  }

  print() {
    console.log("Async Operations Timeline:");
    for (const [name, timing] of this.tasks) {
      const duration = timing.end ? timing.end - timing.start : "ongoing";
      console.log(`${name}: ${duration}ms`);
    }
  }
}

// Usage example
const visualizer = new AsyncVisualizer();

visualizer.start("API request");
await fetch("/api").then(() => {
  visualizer.end("API request");
});

visualizer.start("Data processing");
await new Promise(resolve => setTimeout(resolve, 1000));
visualizer.end("Data processing");

visualizer.print();

Deno-Specific Async Patterns

Web Workers Async Communication

// Main thread code
const worker = new Worker(new URL("./worker.ts", import.meta.url).href, {
  type: "module",
  deno: { namespace: true }
});

worker.postMessage({ type: "calculate", data: [1, 2, 3, 4, 5] });

worker.onmessage = (e) => {
  console.log("Worker result:", e.data);
};

worker.onerror = (e) => {
  console.error("Worker error:", e.message);
};

// worker.ts
self.onmessage = async (e) => {
  if (e.data.type === "calculate") {
    // Simulate time-consuming computation
    await new Promise(resolve => setTimeout(resolve, 1000));
    const result = e.data.data.reduce((sum, num) => sum + num, 0);
    self.postMessage({ result });
  }
};

Async File System Monitoring

// Use Deno.watchFs for file monitoring
const watcher = Deno.watchFs(["./data"]);

(async () => {
  for await (const event of watcher) {
    console.log("File system event:", event);

    if (event.kind === "create") {
      const content = await Deno.readTextFile(event.paths[0]);
      console.log("New file content:", content);
    }
  }
})();

// Note: Requires --allow-read permission

Share your love