Skip to content

Globals and Built-ins

These are the global APIs that Mikro.js adds on top of the standard QuickJS environment. For standard JavaScript built-ins (Array, Object, Promise, Map, Set, etc.), see the QuickJS-NG documentation.

Timers

ts
function setTimeout(callback: (...args: any[]) => void, ms?: number): number
function clearTimeout(id: number): void

function setInterval(callback: (...args: any[]) => void, ms?: number): number
function clearInterval(id: number): void

Standard timer functions. These drive the Mikro.js event loop.

console

ts
const console: {
  log(...args: unknown[]): void
  info(...args: unknown[]): void
  warn(...args: unknown[]): void
  error(...args: unknown[]): void
}

Logs to the serial console on device, or stdout in the simulator.

btoa / atob

ts
function btoa(data: string): string
function atob(data: string): string

Base64 encoding and decoding. btoa encodes a binary (latin1) string to base64. atob decodes a base64 string back.

ts
btoa('Hello') // "SGVsbG8="
atob('SGVsbG8=') // "Hello"

btoa throws a RangeError if the string contains characters outside the latin1 range. atob throws a SyntaxError on invalid base64 input.

TextEncoder / TextDecoder

ts
class TextEncoder {
  encode(input?: string): Uint8Array
}

class TextDecoder {
  decode(input?: ArrayBufferView | ArrayBuffer): string
}

UTF-8 encoding and decoding.

AbortController / AbortSignal

ts
class AbortController {
  readonly signal: AbortSignal
  abort(reason?: unknown): void
}

class AbortSignal {
  readonly aborted: boolean
  readonly reason: unknown
  throwIfAborted(): void
  onabort: (() => void) | null
  addEventListener(type: 'abort', fn: () => void): void
  removeEventListener(type: 'abort', fn: () => void): void
  static abort(reason?: unknown): AbortSignal
  static timeout(ms: number): AbortSignal
  static any(signals: AbortSignal[]): AbortSignal
}

Cooperative cancellation. Most useful with fetch for request timeouts:

ts
import {fetch} from 'mikrojs/fetch'

// Cancel if no response within 5 seconds
const result = await fetch('https://api.example.com/data', {
  signal: AbortSignal.timeout(5000),
})

if (!result.ok && result.error.name === 'Aborted') {
  console.error('Request timed out')
}

Manual cancellation:

ts
const controller = new AbortController()
setTimeout(() => controller.abort(), 3000)

const result = await fetch('https://slow.example.com', {
  signal: controller.signal,
})

On ESP32, aborting a fetch cancels the underlying HTTP task and frees its TLS buffers immediately, which can reclaim 16-40 KB of heap.

AbortError / TimeoutError

The default abort reason is an AbortError, and AbortSignal.timeout() uses a TimeoutError. Both are Error subclasses:

ts
const signal = AbortSignal.abort()
signal.reason instanceof AbortError // true
signal.reason.name // "AbortError"

const timeout = AbortSignal.timeout(1000)
// after timeout fires:
timeout.reason instanceof TimeoutError // true
timeout.reason.name // "TimeoutError"

Difference from web standard

Browsers use DOMException with name: "AbortError" and name: "TimeoutError". Mikro.js uses dedicated AbortError and TimeoutError classes instead, because DOMException is a DOM API with a large spec surface that doesn't belong on a microcontroller. Code that checks error.name === "AbortError" works the same way in both environments.

AbortSignal.any() combines multiple signals:

ts
const timeout = AbortSignal.timeout(10000)
const manual = new AbortController()

const result = await fetch(url, {
  signal: AbortSignal.any([timeout.signal, manual.signal]),
})

import.meta

Available inside ES modules:

PropertyTypeDescription
import.meta.urlstringURL of the current module
import.meta.mainbooleanWhether this is the entry module
import.meta.dirnamestringDirectory of the current module
import.meta.basenamestringFilename of the current module
import.meta.pathstringFull path of the current module
import.meta.envRecord<string, string>Environment variables (from NVS on device)

dirname, basename, path, and env are only available for file-based modules (not built-in mik: modules).

Why isn't fetch a global?

In browsers and Node.js, fetch is a global function. In Mikro.js, you import it from mikrojs/fetch instead. This is because Mikro.js's fetch returns a Result instead of throwing on network errors, so the signature is different from the standard fetch. Making it a global would be misleading since it doesn't behave the same way.

ts
import {fetch} from 'mikrojs/fetch'

const result = await fetch('https://example.com')
if (!result.ok) {
  // network error, not an exception
}

WinterTC compatibility

Mikro.js is not WinterTC compatible, and full compliance is not a goal. Where feasible, we align with WinterTC APIs (e.g., fetch, TextEncoder, TextDecoder, AbortSignal, timers), but the runtime is designed for microcontrollers, not server-side JavaScript, and some APIs don't make sense in this context.