Lesson 02-Bun.js Basic Programming

Basic Syntax and APIs

Module Import and Export

ESM and CommonJS Mixed Usage:

// ESM standard imports
import fs from 'fs/promises'  // Default import
import { readFile } from 'fs/promises'  // Named import
import * as utils from './utils.mjs'  // Namespace import

// CommonJS compatible imports
const path = require('path')
const express = require('express')

// Dynamic import (returns Promise)
const dynamicModule = await import('./dynamic.mjs')

// Export methods
export default function main() {}  // Default export
export const pi = 3.14  // Named export
export { foo as bar } from './module'  // Renamed export

Module Resolution Rules:

  1. Prioritizes lookup in node_modules/.bun/install/cache
  2. Supports package.json exports field
  3. Automatically converts CommonJS to ESM

File System Operations

Synchronous vs Asynchronous APIs:

// Asynchronous API (Promise-based)
const data = await Bun.file('package.json').json()
await Bun.write('output.txt', 'Hello World')

// Synchronous API (blocking)
const data = Bun.file('package.json').jsonSync()
Bun.writeSync('output.txt', 'Hello World')

// Advanced file operations
const mmap = await Bun.file('large.bin').mmap()  // Memory mapping
const stats = await Bun.stat('file.txt')  // Get file stats
await Bun.mkdir('dir', { recursive: true })  // Recursive directory creation

Performance Comparison Test:

# File read speed test (1GB file)
Bun: 1.2s
Node.js: 3.5s

Network Programming Basics

HTTP Server Implementation:

import { serve } from 'bun'

const server = serve({
  port: 3000,
  fetch(request) {
    return new Response('Hello Bun!', {
      headers: { 'Content-Type': 'text/plain' }
    })
  }
})

console.log(`Server running at http://localhost:${server.port}`)

HTTP Client Requests:

const response = await fetch('https://api.example.com/data', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ key: 'value' })
})

const data = await response.json()
console.log(data)

Timers and Event Loop

Timer Usage:

// Basic timer
const timerId = setTimeout(() => {
  console.log('Timeout executed')
}, 1000)

// Clear timer
clearTimeout(timerId)

// Interval timer
const intervalId = setInterval(() => {
  console.log('Interval tick')
}, 1000)

// Clear interval
clearInterval(intervalId)

// Immediate execution
setImmediate(() => {
  console.log('Immediate execution')
})

Event Loop Characteristics:

  1. Microtasks have priority over macrotasks
  2. Promise callbacks are microtasks
  3. setTimeout/setInterval are macrotasks

Processes and Subprocesses

Subprocess Management:

import { spawn, exec } from 'bun'

// Start subprocess
const child = spawn(['ls', '-la'], {
  stdio: 'inherit'  // Share stdio
})

// Wait for subprocess to exit
const exitCode = await child.exited

// Execute command and get output
const { stdout, stderr } = await exec('echo Hello Bun!')
console.log(stdout)

Inter-Process Communication:

const { fork } = require('bun')  // Compatible with Node.js API

const child = fork('worker.js')
child.postMessage({ task: 'calculate' })

child.on('message', (msg) => {
  console.log('Received:', msg)
})

Built-in Toolchain Usage

Bun Package Manager

Common Commands:

# Install dependencies
bun install  # Install package.json dependencies
bun add lodash  # Install specific package

# Uninstall dependencies
bun remove lodash

# Update dependencies
bun upgrade  # Update all dependencies
bun upgrade lodash  # Update specific package

# Manage cache
bun cache clean  # Clear cache

Lockfile Features:

  • Uses .bun.lockb binary file
  • More efficient than package-lock.json
  • Supports atomic installation operations

Bun Testing Framework

Writing Test Cases:

// test/example.test.ts
import { describe, it, expect } from 'bun:test'

describe('Math functions', () => {
  it('should add two numbers', () => {
    expect(1 + 2).toBe(3)
  })

  it('should handle async operations', async () => {
    const result = await fetchData()
    expect(result).toBeDefined()
  })
})

Running Tests:

# Run all tests
bun test

# Run specific file
bun test test/example.test.ts

# Generate coverage report
bun test --coverage

Bun Bundler

Bundling Configuration:

# Basic bundling
bun build src/index.ts --outfile dist/bundle.js

# Production optimization
bun build src/index.ts \
  --outfile dist/bundle.js \
  --target browser \
  --minify \
  --sourcemap

Common Options:

OptionDescription
--targetTarget environment (browser/node)
--minifyCode minification
--sourcemapGenerate sourcemap
--watchWatch for file changes

Bun Script Execution

package.json Configuration:

{
  "scripts": {
    "dev": "bun run src/index.ts",
    "test": "bun test",
    "build": "bun build src/index.ts --outfile dist/bundle.js"
  }
}

Executing Scripts:

# Run script
bun run dev

# Run file directly
bun src/index.ts

# Run in background
bun run --detached server.ts

Bun REPL

Interactive Environment:

# Start REPL
bun repl

# Use modules in REPL
> import { serve } from 'bun'
> const server = serve({ port: 3000 })

REPL Features:

  1. Supports ESM module imports
  2. Command history
  3. Multi-line code input
  4. Autocompletion

Asynchronous Programming

Promises and async/await

Basic Usage:

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

// async/await syntax
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data')
    const data = await response.json()
    console.log(data)
  } catch (error) {
    console.error(error)
  }
}

Parallel Execution:

// Promise.all for parallel execution
const [user, posts] = await Promise.all([
  fetch('/user').then(res => res.json()),
  fetch('/posts').then(res => res.json())
])

// Promise.race for racing
const result = await Promise.race([
  fetch('/fast-api'),
  new Promise((_, reject) => 
    setTimeout(() => reject(new Error('Timeout')), 5000)
  )
])

Event-Driven Programming

EventEmitter Usage:

import { EventEmitter } from 'events'

class MyEmitter extends EventEmitter {}
const emitter = new MyEmitter()

// Register event listener
emitter.on('event', (arg1, arg2) => {
  console.log('Event received', arg1, arg2)
})

// Emit event
emitter.emit('event', 'arg1', 'arg2')

// One-time event
emitter.once('once-event', () => {
  console.log('This will only fire once')
})

Custom Event System:

class TaskQueue extends EventEmitter {
  constructor() {
    super()
    this.queue = []
  }

  addTask(task) {
    this.queue.push(task)
    this.emit('taskAdded')
  }

  processTasks() {
    while (this.queue.length) {
      const task = this.queue.shift()
      this.emit('taskStart', task)
      task()
      this.emit('taskComplete', task)
    }
  }
}

Asynchronous I/O Operations

File System Asynchronous Operations:

// File reading
const data = await Bun.file('data.json').json()

// File writing
await Bun.write('output.txt', 'Hello World')

// Directory operations
const files = await Bun.readdir('src')
await Bun.mkdir('dist', { recursive: true })

Network Request Asynchronous Operations:

// HTTP request
const response = await fetch('https://api.example.com/data')
const json = await response.json()

// WebSocket communication
const socket = new Bun.WebSocket('ws://localhost:8080')
socket.onmessage = (event) => {
  console.log('Received:', event.data)
}
socket.send('Hello Server')

Concurrency Control

Advanced Concurrency Patterns:

// Limit concurrency
const limit = (concurrency) => {
  const queue = []
  let active = 0

  const next = () => {
    if (active >= concurrency || !queue.length) return
    active++
    const { task, resolve, reject } = queue.shift()
    task().then(resolve, reject).finally(() => {
      active--
      next()
    })
  }

  return (task) => new Promise((resolve, reject) => {
    queue.push({ task, resolve, reject })
    next()
  })
}

// Usage example
const limitedFetch = limit(3)
await Promise.all(urls.map(url => limitedFetch(() => fetch(url))))

Task Batching:

// Batch process tasks
async function batchProcess(items, batchSize, processItem) {
  const results = []
  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize)
    const batchResults = await Promise.all(
      batch.map(item => processItem(item))
    )
    results.push(...batchResults)
  }
  return results
}

// Usage example
const results = await batchProcess(
  urls,
  5,
  url => fetch(url).then(res => res.json())
)

Asynchronous Error Handling

Error Handling Patterns:

// Basic try/catch
async function riskyOperation() {
  try {
    const result = await unsafeOperation()
    return result
  } catch (error) {
    console.error('Operation failed:', error)
    throw error  // Re-throw error
  }
}

// Promise error handling
fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`)
    }
    return response.json()
  })
  .catch(error => {
    console.error('Fetch failed:', error)
    // Return default value or re-throw error
  })

// Global error handling
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason)
})
Share your love