Queue
The Queue
component provides an API to enqueue elements and defines events to process queued elements in a FIFO order.
The component provides context values with which you can access some internal properties:
$completedItems
: A list containing the queue items that have been completed (fully processed).$queuedItems
: A list containing the items waiting in the queue, icluding the completed items.
Using Queue
As its name suggests, the Queue
component keeps a queue of items to process. You can add items to the queue with the enqueueItem
(or enqueueItems
) method. Once the queue has some items to process, the engine reads them one by one (in FIFO order) and processes an item with the process
event handler.
Though Queue
is a non-visual component, it can render UI for reporting progress through its progressFeedback
property. Also, when the queue gets empty, the UI can render a result summary through the resultFeedback
property.
The following sample demonstrates these concepts. When the user clicks the button, a new random number is queued. The sample imitates a long calculation by waiting one second within the process
event handler and reports the progress.
A ChangeListener
instance observes queue length changes and stores the actual length to display that in the UI.
<App
var.queued="{0}"
var.queueLength="{0}"
var.processed="{0}"
var.result="{0}">
<Button
label="Add a new item to the queue"
onClick="{myQueue.enqueueItem(Math.random()); queued++; }" />
<Queue id="myQueue"
onProcess="processing =>
{
result += processing.item;
delay(1000);
processed++;
processing.onProgress(processed)
}
">
<property name="progressFeedback">
<Text value="{processed} / {queued}" />
</property>
<property name="resultFeedback">
<Text value="{result.toFixed(4)}" />
</property>
</Queue>
<ChangeListener
listenTo="{myQueue.getQueueLength()}"
onDidChange="l => queueLength = l.newValue;"/>
<Text>Items queued: {queued}</Text>
<Text>Current queue length: {queueLength}</Text>
<Text>Current result: {result.toFixed(4)}</Text>
</App>
Try the app by clicking the button several times. Check how the queue processes the items and displays feedback.
Properties
clearAfterFinish
This property indicates the completed items (successful or error) should be removed from the queue after completion.
progressFeedback
This property defines the component template of the UI that displays progress information whenever, the queue's progressReport
function in invoked.
resultFeedback
This property defines the component template of the UI that displays result information when the queue becomes empty after processing all queued items.
Events
complete
The queue fires this event when the queue gets empty after processing all items. The event handler has no arguments.
The following sample displays a tick mark every time the queue is emptied:
<App
var.queued="{0}"
var.queueEmptied=""
var.result="{0}">
<Button
label="Add a new item to the queue"
onClick="{myQueue.enqueueItem(Math.random()); queued++; }" />
<Queue id="myQueue"
onProcess="processing => {
result += processing.item;
delay(1000);
}"
onComplete="queueEmptied += '✅'" >
<property name="progressFeedback">
<Text value="Completed: {$completedItems.length} of {$queuedItems.length}"/>
</property>
<property name="resultFeedback">
<Text value="{result.toFixed(4)}" />
</property>
</Queue>
<Text>Items queued: {queued}</Text>
<Text>Current result: {result.toFixed(4)}</Text>
<Text>Queue emptied: {queueEmptied}</Text>
</App>
didProcess
This event is fired when the processing of a queued item has been successfully processed.
The parameter of the event handler is an object with these properties:
item
: the item to processactionItemId
: The unique (internal) ID of the item being processed, as generated by theQueue
component. You can pass this ID to some API methods (for example, toremove
).processItemContext
: A context object (initially empty) that you can use to add some context-specific information to the item. The event handlers of other events will see this information as the item being processed conveys it.
The following sample uses the didProcess
event handler to add a tick symbol to the progress whenever an item has been processed:
<App
var.queued="{0}"
var.progressLine=""
var.result="{0}">
<Button
label="Add a new item to the queue"
onClick="{myQueue.enqueueItem(Math.random()); queued++; }" />
<Queue id="myQueue"
onProcess="processing => {
result += processing.item;
delay(1000);
}"
onDidProcess="progressLine += '✅'" >
<property name="resultFeedback">
<Text value="{result.toFixed(4)}" />
</property>
</Queue>
<Text>Items queued: {queued}</Text>
<Text>Current result: {result.toFixed(4)}</Text>
<Text>Progress: {progressLine}</Text>
</App>
process
This event is fired to process the next item in the queue. If the processing cannot proceed because of some error, raise an exception, and the queue will handle that.
The parameter of the event handler is an object with these properties:
item
: the item to processactionItemId
: The unique (internal) ID of the item being processed, as generated by theQueue
component. You can pass this ID to some API methods (for example, toremove
).processItemContext
: A context object (initially empty) that you can use to add some context-specific information to the item. The event handlers of other events will see this information as the item being processed conveys it.reportProgress
: A function you can use to report the progress. Invoke this method with an argument that theprogressFeedback
component's template will utilize.
See the example in the Using Queue section.
processError
This event is fired when processing an item raises an error. The event handler method receives two parameters. The first is the error raised during the processing of the item; the second is an object with these properties:
item
: the item to processactionItemId
: The unique (internal) ID of the item being processed, as generated by theQueue
component. You can pass this ID to some API methods (for example, toremove
).processItemContext
: A context object (initially empty) that you can use to add some context-specific information to the item. The event handlers of other events will see this information as the item being processed conveys it.
If the event handler returns false, the queue does not sign the error in the UI. With other return values (including no return value), the queue displays the error.
The following sample generates an error for every fourth item, and gives an error feedback with the processError
event handler:
<App
var.queued="{0}"
var.progressLine=""
var.result="{0}">
<Button
label="Add a new item to the queue"
onClick="{myQueue.enqueueItem(Math.random()); queued++; }" />
<Queue id="myQueue"
onProcess="processing => {
if (progressLine.length % 4 === 3) {
throw 'Item cannot be processed';
}
result += processing.item;
delay(1000);
}"
onDidProcess="progressLine += '✅'"
onProcessError="progressLine += '❌'" >
<property name="resultFeedback">
<Text value="{result.toFixed(4)}" />
</property>
</Queue>
<Text>Items queued: {queued}</Text>
<Text>Current result: {result.toFixed(4)}</Text>
<Text>Progress: {progressLine}</Text>
</App>
Click the button several times to see how processing errors are handled in the UI.
willProcess
This event is triggered to process a particular item.
This event is fired before the next item in the queue gets processed. If the event handler returns false
, the queue skips processing that item. With other return values (including no return value), the queue continues processing the item.
The parameter of the event handler is an object with these properties:
item
: the item to processactionItemId
: The unique (internal) ID of the item being processed, as generated by theQueue
component. You can pass this ID to some API methods (for example, toremove
).processItemContext
: A context object (initially empty) that you can use to add some context-specific information to the item. The event handlers of other events will see this information as the item being processed conveys it.
The following sample declares a willProcess
event handler that will skip processing (summing) items less than 0.5; the handler counts the number of skipped items.
<App
var.queued="{0}"
var.skipped="{0}"
var.result="{0}">
<Button
label="Add a new item to the queue"
onClick="{myQueue.enqueueItem(Math.random()); queued++; }" />
<Queue id="myQueue"
onWillProcess="toProcess => toProcess.item < 0.5 ? (skipped++, false) : true"
onProcess="processing => {
result += processing.item;
delay(1000);
}">
<property name="resultFeedback">
<Text value="{result.toFixed(4)}" />
</property>
</Queue>
<Text>Items queued: {queued}</Text>
<Text>Items skipped: {skipped}</Text>
<Text>Current result: {result.toFixed(4)}</Text>
</App>
Click the button several times and see how the number of skipped items increments.
Exposed Methods
enqueueItem
This method enqueues the item passed in the method parameter. The new item will be processed after the current queue items have been handled. The method retrieves the unique ID of the newly added item; this ID can be used later in other methods, such as remove
.
The following example stores and displays this item when a new item is put into the queue:
<App var.queued="{0}" var.itemIds="">
<Button
label="Add a new item to the queue"
onClick="{
const itemId = myQueue.enqueueItem(Math.random());
itemIds += itemId + ', '; queued++;
}" />
<Queue id="myQueue" onProcess="processing => {}" />
<Text>Items queued: {queued}</Text>
<Text>Item IDs: {itemIds}</Text>
</App>
enqueueItems
This method enqueues the array of items passed in the method parameter. The new items will be processed after the current queue items have been handled. The method retrieves an array of unique IDs, one for each new item. An item ID can be used later in other methods, such as remove
.
getQueueLength
This method retrieves the current queue length. The queue contains only those items that are not fully processed yet.
getQueuedItems
You can use this method to return the items in the queue. These items contain all entries not removed from the queue yet, including pending, in-progress, and completed items.
This method returns the items currently in the queue with their entire context. The result of this method is a list of objects with these properties:
item
: The queued item (as passed to theenqueueItem
andenqueueItems
methods)actionItemId
: The unique (internal) ID of the item being processed, as generated by theQueue
component.status
: The current processing status of the item; one of the following values:"pending"
,"started"
,"in-progress"
,"completed"
, or"error"
progress
: The latest progress value reported for the item (may be undefined)result
: The optional value theprocess
event handler returns.
Note: When all items are processed, the queue removes the items and fires the
complete
event. When the event handler runs, the queue is empty, andgetQueuedItems
returns an empty list.
remove
This method retrieves the current queue length. The queue contains only those items that are not fully processed yet.
This method removes an item from the queue. The parameter of this method is the unique ID of the item returned from the enqueueItem
(or enqueueItems
method.
The following example emulates a file-processing application. When a file is about to save (in the process
event), the processing fails with files that deny overwrite. In such a case, the processError
event handler asks the user to confirm the overwrite action. In case of confirmation, the queue uses the remove
action to discard the faulty queue item and enqueues the file again with the accept
behavior so that it won't fail next time.
<App var.queued="{0}" var.processed="" var.skipped="{0}">
<Button
label="Add a new file to the queue"
onClick="{myQueue.enqueueItem({file: ++queued, conflict: 'deny'})}" />
<Queue id="myQueue"
onProcess="processing =>
{
delay(1000);
if (processing.item.conflict === 'deny') {
throw 'Conflict';
}
processed += processing.item.file + ', ';
}
"
onProcessError="(error, processing) => {
if (error.message === 'Conflict') {
console.log(error);
const result = confirm('Do you want to overwrite?',
'File ' + processing.item.file + ' already exists',
'Overwrite');
$this.remove(processing.actionItemId);
if (result) {
$this.enqueueItems([{...processing.item, conflict: 'accept'}]);
}
return false;
}
}">
<property name="resultFeedback">
<Text value="All items processed" />
</property>
</Queue>
<Text>Items queued: {queued}</Text>
<Text>Processed: {processed}</Text>
</App>
Styling
This component does not have any styles.