Stop an export, a load or a save that your server no longer needs, or give it
a deadline, with a standard AbortSignal.
Exports, loads, downloads and saves go on in the engine after the call returns. When a client disconnects or a job
runs out of time, your server no longer needs that work. Each engine call that exports, loads, downloads or saves
takes an abortSignal option. An abort ends the call at once, stops its work in the engine and frees what it
holds. On the server you cancel in code, because there is no user interface.
This guide covers how to cancel a call, which calls you can cancel, how to give a call a deadline, how to cancel from a progress callback, and what the engine keeps after a cancel.
Cancelling a Call#
We start with a page that holds an image block. The image still downloads when the export starts, so the export waits for it, and the abort has work to stop.
const scene = engine.scene.create();const page = engine.block.create('page');engine.block.setWidth(page, 800);engine.block.setHeight(page, 600);engine.block.appendChild(scene, page);
// An image fill instead of addImage(), because addImage() waits for the download.// An export with nothing to wait for ends inside its call, before the abort.const imageBlock = engine.block.create('graphic');engine.block.setShape(imageBlock, engine.block.createShape('rect'));const imageFill = engine.block.createFill('image');engine.block.setString(imageFill, 'fill/image/imageFileURI', IMAGE_URL);engine.block.setFill(imageBlock, imageFill);engine.block.setWidth(imageBlock, 800);engine.block.setHeight(imageBlock, 600);engine.block.appendChild(page, imageBlock);Create an AbortController and pass its signal as abortSignal in the options of the call. Call
controller.abort() to cancel. The promise rejects with Error('AbortSignal received').
const controller = new AbortController();const exporting = engine.block.export(page, { mimeType: 'image/png', abortSignal: controller.signal});controller.abort();
try { await exporting; console.log('The export ended before the abort');} catch (error) { // Logs "AbortSignal received" console.log('The export stopped:', (error as Error).message);}A signal that aborted before the call rejects at once, and the call starts nothing. A call that has already settled ignores the abort. An export with nothing to load can end inside its call, so an abort on the next line comes too late, and the promise resolves with the file.
Calls That Can Be Cancelled#
Each of these calls takes abortSignal in its options object, which is its last argument. In
engine.scene.createFromImage() the options object is the seventh argument, after spacingInScreenSpace. In
engine.block.saveToString() it is the fourth, after onDisallowedResourceScheme.
- Exports:
engine.block.export()(images and PDF),engine.block.exportWithColorMask(),engine.block.exportVideo(),engine.block.exportAudio() - Scene and block loads:
engine.scene.load(),engine.scene.loadFromURL(),engine.scene.loadFromString()withwaitForResources,engine.scene.loadFromArchiveURL(),engine.scene.applyTemplateFromURL(),engine.block.loadFromURL(),engine.block.loadFromArchiveURL() - New scenes from media:
engine.scene.createFromImage(),engine.scene.createFromVideo() - Downloads for a block or an asset:
engine.block.createCaptionsFromURI(),engine.block.addImageFileURIToSourceSet(),engine.block.addVideoFileURIToSourceSet(),engine.block.forceLoadAVResource(),engine.block.forceLoadResources(),engine.asset.addLocalAssetSourceFromJSONURI(),engine.asset.apply(),engine.asset.defaultApplyAsset() - Lookups:
engine.editor.getMimeType(),engine.editor.getFontMetrics(),engine.block.getDominantColors() - Color profiles:
engine.scene.setCMYKProfile(),engine.editor.loadCMYKProfile() - Saves:
engine.scene.saveToArchive(),engine.block.saveToArchive(), andengine.scene.saveToString()orengine.block.saveToString()while they wait foronDisallowedResourceScheme
@cesdk/node has no video export, so its exportVideo() throws. The native package @cesdk/node-native exports
video, and its exportVideo() takes abortSignal too.
Giving a Call a Deadline#
Pass AbortSignal.timeout(ms) as abortSignal. The engine has no timeout of its own, so the signal is the
deadline. Here we give the scene load ten seconds.
const deadline = AbortSignal.timeout(10_000);try { await engine.scene.load(SCENE_URL, { abortSignal: deadline }); console.log('The scene loaded in time');} catch (error) { if (!deadline.aborted) { throw error; } console.log('The scene did not load within 10 seconds');}A deadline rejects with the same AbortSignal received error as abort(). Check aborted on the signal to tell
a missed deadline from a failed download, and throw every other error again.
Cancelling From a Progress Callback#
You can call controller.abort() inside onProgress of exportAudio(). @cesdk/node has no video export, so we
show the cancel with a WAV export of a page that holds audio. Here we stop an export that has rendered less than
half of its frames after one minute.
const controller = new AbortController();const startedAt = Date.now();try { const blob = await engine.block.exportAudio(page, { mimeType: 'audio/wav', abortSignal: controller.signal, onProgress: (renderedFrames, encodedFrames, totalFrames) => { const elapsed = Date.now() - startedAt; if (elapsed > 60_000 && renderedFrames < totalFrames / 2) { controller.abort(); } } }); console.log(`The WAV file has ${blob.size} bytes`);} catch (error) { if (!controller.signal.aborted) { throw error; } console.log('The export was too slow and stopped');}No progress report follows the abort, and the next export can start at once. The time of the first reports includes the start-up of the export, so a projection of the total time from them would stop fast exports too.
What a Cancel Stops and What It Keeps#
A cancel stops the work of the call, but the engine keeps what other blocks and calls still need:
- A video export and an audio export that mixes a page stop at once and release their encoders.
- An image or PDF export frees its document in the next engine update.
- An audio export that copies the audio of one block without the mixer finishes the copy in the background and drops it.
- A load stops its download, unless a block or another call needs the same file. The engine reads every video fill at each update, so the download of a video fill goes on.
load()andloadFromURL()keep the current scene. WithwaitForResourcesthe new scene replaces it before the wait for its resources, so a cancel during that wait keeps the new scene.loadFromString()withwaitForResourcesloads the scene at once, so a cancel ends only the wait.loadCMYKProfile()ends only the wait, because the document keeps its profile loaded.- A cancelled
setCMYKProfile()gives back its claim, so an assignment that it replaced still takes effect. - An answer of
onDisallowedResourceSchemeafter the abort changes nothing. asset.apply()stops the download that the default apply waits for, so it creates no block. An asset source with its ownapplyAssetkeeps running in your code. A block that it creates stays, and the call does not return it.getDominantColors()waits while a resource of the block loads, so a signal gives it a deadline.
Starting the Next Call#
After the promise rejects, the engine is free for the next export or load. Start it in the rejection handler or
after the await. Here we export the same page again, this time without a signal.
const blob = await engine.block.export(page, { mimeType: 'image/png' });console.log(`The next export made a PNG file of ${blob.size} bytes`);The second export waits for the image that the first export waited for, because the image block still needs it.
An audio export that starts inside the progress callback of another audio export fails with the
ENCODE.AUDIO_CONCURRENT_ENCODING error. Start it after the running export settles.
Troubleshooting#
- The promise rejects with
AbortSignal receivedat once: The signal aborted before the call. Create a newAbortControllerfor each call. - The promise resolves although you aborted: The call ended before the abort. An export with nothing to load can end inside its call.
- The download of a cancelled load goes on: Another block or another call needs the same file.
- An audio export fails with
ENCODE.AUDIO_CONCURRENT_ENCODING: It started inside the progress callback of a running audio export. - A call rejects with a validation error for
abortSignal: The option must be anAbortSignal, not a boolean or a string.
API Reference#
These are the methods this guide calls. The section Calls That Can Be Cancelled lists every method that takes
abortSignal.
| Method | Category | Purpose |
|---|---|---|
engine.block.export() |
Block | Export a block as an image or a PDF |
engine.block.exportAudio() |
Block | Export the audio of a block |
engine.scene.load() |
Scene | Load a scene or an archive from a URL or a string |
Next Steps#
- Export - Export designs and videos
- Resources - Learn how the engine loads files
- Scenes - Load and save scenes
- Error Catalog - Look up engine error codes