Copyright 2017-2024 Moddable Tech, Inc.
Revised: January 19, 2024
The Timer
class provides both time-based callbacks and a delay function.
import Timer from "timer";
Timer callbacks are invoked with this
set to globalThis
. Use an arrow function or Function.prototype.bind
to bind the callback's this
to another value.
Each timer has two intervals: the initial interval and a repeat interval. The intervals are in milliseconds. The initial interval is the time until the callback is first invoked. The repeat interval is the time between successive invocations of the callback after the initial interval. The API reference that follows indicates how each API modifies the initial and repeat intervals.
If the repeat interval is zero when the timer's callback returns, the timer is automatically cleared and can no longer be used. The repeat interval may be changed by the callback using Timer.schedule
.
The Timer.set
and Timer.repeat
functions create a new timer and return the ID of the timer. Timer IDs are opaque that are only useful for passing to Timer
functions.
Timer callbacks can provide the basic behaviors of setImmediate
, setTimeout
and setInterval
. The timers example shows how to do this.
Timer.set(callback[, initialInterval, repeatInterval])
The set
function requests a function be called once after a certain period. Timer.set
returns the new timer's ID.
An immediate timer is called on the next cycle through the run loop. To set an immediate timer, call set
with a single argument.
Timer.set(id => trace("immediate fired\n"));
A one shot timer is called once after a specified number of milliseconds. If the number of milliseconds is zero, a one shot timer is equivalent to an immediate timer.
Timer.set(id => trace("one shot fired\n"), 1000);
Calling set
with a repeat
is equivalent to a repeating timer with the first callback triggered after the interval
.
Timer.set(id => trace("repeat fired\n"), 1000, 100);
The callback function receives the timer id as the first argument.
If Timer.set
is called without the initial interval and repeat interval, it is an immediate one-shot timer (initial and repeat intervals are set to 0). If Timer.set
is called with only an initial interval, it is a one-shot timer (repeat interval is set to 0). If Timer.set
is called with both an initial and a non-zero repeat interval, it is a repeating timer.
Timer.repeat(callback, repeatInterval)
A repeating timer is called continuously until stopped using the Timer.clear
function. Timer.repeat
returns the new timer's ID.
Timer.repeat(id => trace("repeat fired\n"), 1000);
The callback function receives the timer id as the first argument.
This function sets both the initial interval and repeat interval to the value specified by repeatInterval
. Use Timer.set
to create a timer with an initial interval that is different from the repeat interval.
Timer.schedule(id [, initialInterval[, repeatInterval]])
The schedule
function reschedules or unschedules an existing timer.
If called with an initial interval but no repeat interval, the timer behaves like a one shot timer created with Timer.set
. If called with both an initial interval and non-zero repeat interval, it behaves like a repeating timer created with Timer.set
with both initial interval and repeat interval arguments. If called without interval arguments, the timer is unscheduled and will not trigger until rescheduled using Timer.schedule
(an unscheduled timer is considered to have infinite initial and repeat intervals).
In the following example, the callback function is triggered twice at one second intervals and then rescheduled to once every two seconds.
let state = 0;
Timer.repeat(id => {
if (0 == state)
state = 1;
else if (1 == state) {
state = 2;
Timer.schedule(id, 2000, 2000);
}
}, 1000);
When Timer.schedule
is used to set the initial interval, the callback is next invoked after the new initial interval has elapsed.
Note: If the next trigger time is unknown, unscheduling a timer is preferred to scheduling for a long time in the future. Unscheduling and rescheduling a timer can more efficient than clearing a timer and later allocating a new one.
The clear
function cancels a timer. The Timer.set
and Timer.repeat
functions returns the ID for a timer, which is then passed to clear.
let aTimer = Timer.set(id => trace("one shot\n"), 1000);
Timer.clear(aTimer);
Note: Immediate and one shot timers are automatically cleared after invoking their callback function. There is no need to call clear
except to cancel the timer before it fires.
Note: If Timer.clear
is passed a value of undefined
or null
for the ID, no exception is generated.
The delay
function delays execution for the specified number of milliseconds.
Timer.delay(500); // delay 1/2 second
Note: In general, the preferred style of JavaScript programming is to avoid long delays that block execution. Timer.delay
is provided because in embedded development it is common to need short delays when interacting with hardware. For longer delays, using an alternative such as a Timer callback or asynchronous execution with Promises may be more appropriate.
The Time
class provides time functions and a tick counter.
The set
function sets the system time. The seconds
argument corresponds to the number of seconds elapsed since January 1, 1970, i.e. Unix time.
The timezone
property is set to the time zone offset in seconds from UTC.
Time.timezone = +9 * 60 * 60; // Set time zone to UTC+09:00
The dst
property is set to the daylight saving time (DST) offset in seconds.
Time.dst = 60 * 60; // Set DST
The ticks
property returns the value of a millisecond counter. The value returned does not correspond to the time of day. The milliseconds are used to calculate time differences.
const start = Time.ticks;
for (let i = 0; i < 1000; i++)
;
const stop = Time.ticks;
trace(`Operation took ${Time.delta(start, stop)} milliseconds\n`);
On devices that supports multiple concurrent JavaScript virtual machines (for example, using Workers), the clock used to determine the value of the ticks
property is the same across all virtual machines. This allows tick
values created in one machine to be compared with values from another.
The range of the value depends on the host. On most microcontrollers, the value is a signed 32-bit integer. On the simulator, it is a positive 64-bit floating point value. To determine the difference between two ticks
values, use Time.delta()
which is guaranteed to give a correct result for the host.
The delta
function calculates the difference between two values returned by Time.ticks
. It is guaranteed to return a correct result even when the value rolls over. If the optional end
argument is omitted the current value of Time.ticks
is used.
microseconds
property (optional)
The microseconds
property returns the value of a microseconds counter. The value returned does not correspond to the time of day. The microseconds are used to calculate time differences.
To use the microseconds
property, include its manifest in the project manifest:
"include": [
...
"$(MODDABLE)/modules/base/microseconds/manifest.json",
],
The microseconds
property is used in the same way as the ticks
property. Like the ticks
property, a single time source is used when there multiple concurrent virtual machines. The range of the microseconds
property is a 64-bit floating point value.
Unlike Time.ticks
, the values returned by Time.microseconds
may always be subtracted from one another to calculate intervals.
const start = Time.microseconds;
for (let i = 0; i < 1000; i++)
;
const stop = Time.microseconds;
trace(`Operation took ${stop - start} microseconds\n`);
Note: Not all hosts implement microseconds. Hosts that do not implement microseconds generate an error at build time.
The Debug
class provides functions that are useful during the development process.
import Debug from "debug";
The gc
function can be used to turn the JavaScript garbage collector on and off, as well as to run the garbage collector on-demand.
Calling Debug.gc
with no arguments runs the garbage collector immediately.
Calling Debug.gc
with a single boolean argument enables or disables the garbage collector.
Debug.gc(true) // enable garbage collector
Debug.gc(false); // disable garbage collector
The UUID
class provides a single function to generate a UUID string.
Note: Generating a truly unique UUID requires that the device running this function have a unique MAC address and the valid time. Neither of these is guaranteed on all microcontrollers. For production software release, check your device configuration.
The UUID
function returns a new UUID formatted as a string.
let value = UUID(); // 1080B49C-59FC-4A32-A38B-DE7E80117842
function deepEqual(a, b [,options])
The deepEqual
function implements a deep comparison between two JavaScript object.
import deepEqual from "deepEqual";
There is no standard algorithm for deeply comparing two objects in JavaScript, and there are many possible correct approaches. The Moddable SDK adopts the behavior of assert.deepEqual
and assert.deepStrictEqual
from Node.js for its implementation of deepEqual
.
The deepEqual
function has an optional third parameter, an options object. By default the deepEqual
comparison is not-strict, similar to using ==
for comparisons. Passing {strict: true}
for the options object uses strict comparison, similar to using ===
for comparisons.
const a = {a: 1, b: 0, c: "str"};
const b = {a: 1, b: "0", c: "str"};
deepEqual(a, b); // true
deepEqual(a, b, {strict: true}); // false
The known differences between the Moddable SDK implementation and Node.js will not impact most uses:
-
WeakMap
and WeakSet
using read-only objects as keys
- XS does not call
valueOf
to compare boxed primitives
function structuredClone(object[, transferables])
The structuredClone
function creates a deep copy of a JavaScript object.
import structuredClone from "structuredClone";
The structuredClone
function in the Moddable SDK implements the algorithm defined by WHATWG for the web platform as much as practical, including circular references and the transferables
option.
const a = {a: 1, b: Uint8Array.of(1, 2, 3)}
const aCopy = structuredClone(a);
The Moddable SDK implementation of structuredClone
implements all supported types that are part of the JavaScript language standard. It does not, of course, clone object types that are part of the web platform and not present on embedded systems such as DOMException
.
The Instrumentation
class returns statistics on the behavior of the runtime, including memory use, open file count, and rendering frame rate.
import Instrumentation from "instrumentation";
The get
function returns the value of the instrumented item at the index specified by the what
parameter. Instrumented items are consecutively numbered starting at index 1.
let pixelsDrawn = Instrumentation.get(1);
The index of instrumentation items depends on the host and varies between different devices based on the supported features. Use Instrumentation.map()
to determine the index of a specific instrumentation on the running host.
The map
function returns the instrumentation index for a name.
let pixelsDrawnIndex = Instrumentation.map("Pixels Drawn");
let pixelsDrawn = Instrumentation.get(pixelsDrawnIndex);
If the instrumentation item named is unavailable, map
returns undefined
.
The name
function returns the name of the instrumentation item at the specified index. It can be used to iterate through all available instrumentation items.
for (let i = 1; true; i++) {
const name = Instrumentation.name(i);
if (!name)
break;
trace(`${name}: ${Instrumentation.get(i)}\n`);
}
The table below describes the instrumented items that are available. The following instrumented items are reset at one second intervals: Pixels Drawn, Frames Drawn, Poco Display List Used, Piu Command List Used, Network Bytes Read, Network Bytes Written, and Garbage Collection Count.
Name |
Long Description |
Pixels Drawn |
The total number of pixels rendered to by Poco during the current interval. This value includes pixels drawn to a display device and pixels rendered offscreen. |
Frames Drawn |
The total number of frames rendered by Poco during the most recent interval. Frames are counted by calls to Poco.prototype.end() and by Piu frame updates. |
Network Bytes Read |
The total number of bytes received by the Socket module during the current interval. This includes bytes received over TCP and UDP. |
Network Bytes Written |
The total number of bytes send by the Socket module during the current interval. This includes bytes sent using TCP and UDP. |
Network Sockets |
The total number of active network sockets created by the Socket module. This includes TCP sockets, TCP listeners, and UDP sockets. |
Timers |
The number of allocated timers created by the Timer module. |
Files |
The number of open files and directory iterators created the File module. |
Poco Display List Used |
The peak size in bytes of the Poco display list in the current interval. |
Piu Command List Used |
The peak size in bytes of the Piu command list in the current interval. |
Turns |
The number of times the event loop has run in the current interval. |
CPU 0 |
The load on CPU 0 during the current interval. |
CPU 1 |
The load on CPU 1 during the current interval. |
System Free Memory |
The number of free bytes in the system memory heap. This value is not available in the simulator. |
XS Slot Heap Used |
Number of bytes in use in the slot heap of the primary XS machine. Some of these bytes may be freed when the garbage collector next runs. |
XS Chunk Heap Used |
Number of bytes in use in the chunk heap of the primary XS machine. Some of these bytes may be freed when the garbage collector next runs. |
XS Keys Used |
Number of runtime keys allocated by the primary XS machine. Once allocated keys are never deallocated. |
XS Garbage Collection Count |
The number of times the garbage collector has run in the current interval. |
XS Modules Loaded |
The number of JavaScript modules that are currently loaded in the primary XS machine. This number does not include modules which are preloaded. |
XS Stack Used |
The maximum depth in bytes of the stack of the primary XS virtual machine during the current interval. |
XS Promises Settled |
The number of Promises settled. This is useful as a measure of Promisee/async/await activity. |
The Console
class implements a serial terminal for debugging and diagnostic purposes. The Console
module uses CLI
modules to implement the terminal commands.
The CLI
class is a plug-in interface for commands used in a command line interface. CLI
classes implement commands for use by the Console
module (serial command line) and Telnet
module (network command line).
See the Worker documentation for information about the Worker
class.