State Management

The Anyline Web SDK maintains internal state throughout its lifecycle. Understanding state transitions and proper state management is essential for building robust scanning applications.

SDK States

The SDK can be in one of five states at any given time:

enum State {
  INITIALIZED = 'initialized',
  PAUSED = 'paused',
  SCANNING = 'scanning',
  STOPPED = 'stopped',
  DISPOSED = 'disposed'
}

State Descriptions

INITIALIZED

SDK has been initialized via init() but scanning has not started yet. Camera is not active. This is the initial state after calling init().

SCANNING

SDK is actively processing camera frames and scanning for results. Camera stream is active and frames are being processed by the scan engine.

PAUSED

Scanning is temporarily paused. Camera stream remains active but frames are not processed. Can be resumed without reinitializing the camera.

STOPPED

Scanning session has ended. Camera stream may be inactive. Calling resumeScanning() will restart the camera stream if needed.

DISPOSED

SDK has been completely unmounted and cleaned up. All resources are released. Must call init() again to use the SDK.

Checking Current State

Use the getState() method to check the current SDK state:

import { init, State } from '@anyline/anyline-js';

const anyline = init({ /* ... */ });

// Check current state
const currentState = anyline.getState();

if (currentState === State.SCANNING) {
  console.log('Currently scanning');
} else if (currentState === State.INITIALIZED) {
  console.log('Ready to start scanning');
}

State Transition Methods

startScanning()

Starts the scanning process and transitions to SCANNING state.

From States: INITIALIZED, STOPPED, PAUSED

Returns: Promise<HTMLVideoElement | undefined> - Returns HTMLVideoElement when called from INITIALIZED state, or undefined when called from PAUSED or STOPPED state

Behavior:

  • From INITIALIZED: Starts camera, loads assets, begins scanning, and returns the video element

  • From STOPPED or PAUSED: Internally calls resumeScanning() and returns undefined

const anyline = init({ /* ... */ });

// From INITIALIZED → SCANNING
const video = await anyline.startScanning();
console.log(anyline.getState()); // 'scanning'
console.log(video); // HTMLVideoElement

// From STOPPED → SCANNING
anyline.stopScanning();
await anyline.startScanning(); // Returns undefined, no video element
console.log(anyline.getState()); // 'scanning'
When called from STOPPED or PAUSED state, startScanning() internally calls resumeScanning() and does NOT return the video element. If you need the video element reference, call startScanning() only from INITIALIZED state or use a different approach to access the video element.
Calling startScanning() after dispose() rejects with DisposedError. Check the current state (or wrap in a try/catch) before restarting a disposed instance.

pauseScanning()

Temporarily pauses frame processing while keeping the camera active.

From States: SCANNING

Behavior:

  • Stops processing frames

  • Camera stream remains active

  • Can be resumed quickly without camera reinitialization

// From SCANNING → PAUSED
anyline.pauseScanning();
console.log(anyline.getState()); // 'paused'
Calling pauseScanning() from any state other than SCANNING has no effect and logs a debug message.

stopScanning()

Stops the scanning session.

From States: SCANNING, PAUSED

Behavior:

  • Stops frame processing

  • Camera stream may be stopped (implementation detail)

  • Requires camera restart on resume

// From SCANNING → STOPPED
anyline.stopScanning();
console.log(anyline.getState()); // 'stopped'
Calling stopScanning() from any state other than SCANNING or PAUSED has no effect and logs a debug message.

resumeScanning()

Resumes scanning after pause or stop.

From States: PAUSED, STOPPED

Returns: Promise<void>

Behavior:

  • From PAUSED: Immediately resumes frame processing

  • From STOPPED: Restarts camera stream if needed, then resumes

// From PAUSED → SCANNING
await anyline.resumeScanning();
console.log(anyline.getState()); // 'scanning'

// From STOPPED → SCANNING (restarts camera)
await anyline.resumeScanning();
console.log(anyline.getState()); // 'scanning'
Calling resumeScanning() when already in SCANNING state has no effect and logs a debug message.

dispose()

Completely unmounts the SDK and cleans up all resources.

From States: Any state

Behavior:

  • Stops scanning if currently in SCANNING state

  • Terminates worker thread

  • Closes camera stream

  • Clears memory

  • Unmounts UI components

  • Transitions to DISPOSED

// From any state → DISPOSED
anyline.dispose();
console.log(anyline.getState()); // 'disposed'

// Must create new instance to use again
const newAnyline = init({ /* ... */ });
Always call dispose() when done with the SDK to prevent memory leaks. This is especially important in single-page applications.
Calling dispose() when already DISPOSED will still attempt cleanup operations but is safe to call.

Best Practices

  1. Always Check State Before Transitions

    Check getState() before calling state transition methods to avoid no-op calls.

  2. Handle Component Unmounting

    Always call dispose() when your component unmounts to prevent memory leaks.

  3. Choose Pause vs Stop Wisely

    Use pauseScanning() for temporary interruptions, stopScanning() for longer breaks.

  4. Listen to Visibility Changes

    Implement visibility change handlers to automatically manage scanning state.

  5. Handle Disposed State

    Check for DISPOSED state and reinitialize if needed. Once disposed, the instance cannot be reused.

  6. Use State in UI

    Display current state in your UI to give users feedback about scanning status.

  7. Avoid Redundant Calls

    The SDK silently ignores invalid state transitions (e.g., calling pauseScanning() when not scanning), but checking state first is more efficient.

Troubleshooting

State Not Changing After Method Call

Problem: Called a state transition method but state didn’t change.

Possible Causes:

  1. Called from invalid state (e.g., pauseScanning() when not in SCANNING)

  2. Method threw an error that was not caught

  3. Async method not awaited

Solution:

// Check current state
console.log('Before:', anyline.getState());

try {
  await anyline.startScanning();
  console.log('After:', anyline.getState());
} catch (error) {
  console.error('Failed:', error);
}

Cannot Resume After Stop

Problem: resumeScanning() doesn’t work after calling stopScanning().

Solution: resumeScanning() works from both STOPPED and PAUSED states. If it’s not working, check for errors:

anyline.stopScanning();
console.log(anyline.getState()); // Should be 'stopped'

try {
  await anyline.resumeScanning();
  console.log(anyline.getState()); // Should be 'scanning'
} catch (error) {
  console.error('Resume failed:', error);
}

Memory Leaks in SPA

Problem: Application slows down after navigating between pages multiple times.

Solution: Always dispose SDK instances when components unmount:

// Track all instances and dispose them
const instances = [];

function createScanner() {
  const anyline = init({ /* ... */ });
  instances.push(anyline);
  return anyline;
}

// On app shutdown or route change
function cleanup() {
  instances.forEach(instance => {
    if (instance.getState() !== State.DISPOSED) {
      instance.dispose();
    }
  });
  instances.length = 0;
}

See Also