Tasks and jobs

Tasks are bound to online or offline data or services, and provide methods to perform asynchronous operations using those resources. By using tasks you can:

  • Download, collect, and update geographic information using GeodatabaseSyncTask
  • Download and display tiled basemaps using ExportTileCacheTask
  • Locate addresses and find addresses from map locations using LocatorTask
  • Calculate point-to-point or multi-stop routes and get driving directions using RouteTask
  • Perform complex GIS analysis by executing geoprocessing models using GeoprocessingTask

Tasks either return their results directly from asynchronous methods on the task, or make use of jobs to provide status updates and results.


Some operations return their results directly from asynchronous methods on the task, for example LocatorTask.geocode and RouteTask.solveRoute. For more complex or longer running operations, tasks make use of jobs instead.

To use tasks that return results directly:

  1. Create the task by initializing it to use the required data or service.
  2. Define the task inputs.
    • Some operations require only simple value inputs (for example a simple geocode operation may only require an address string as input).
    • Others require parameters to be defined (for example, to limit a geocode operation to a specific country).
  3. Call the async operation method, passing in the inputs you defined.
  4. Use the results from the operation as required, for example to display geocode results on a map.

The code below creates a LocatorTask using Esri's World Geocode server, and creates a GeocodeParameters with details about this geocode operation.

// Create a locator task using the World Geocoding Service
LocatorTask {
    id: locatorTask
    url: "http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer"

    // handle the result once the geocode status is complete
    onGeocodeStatusChanged: {
        if (geocodeStatus === Enums.TaskStatusCompleted) {
            if (geocodeResults.length > 0) {
                var graphic = ArcGISRuntimeEnvironment.createObject("Graphic");
                graphic.geometry = geocodeResults[0].displayLocation;
                graphic.attributes.attributesJson = geocodeResults[0].attributes;

// Create geocode parameters
GeocodeParameters {
    id: geocodeParameters
    minScore: 75
    resultAttributeNames: ["Place_addr", "Match_addr"]
When the address string is available in a text input field, the app calls geocodeWithParameters to start the task.
locatorTask.geocodeWithParameters(textField.text, geocodeParameters);
See the QML sample FindAddress for a complete code example.

Some Tasks are loadable, and will load themselves when you call an asynchronous method (like that shown above) that requires the task to be in a loaded state. Loadable objects are generally required to be loaded before their properties are filled out.

Define input parameters

Tasks offer numerous options that allow you to tailor the operation to your requirements. For example, when geocoding you can restrict your search to a specific area, country, category of place, and/or number of results. When an author publishes a service or packages a resource, they can choose default values for these options that suit the specific data or the most common use case for the service.

To find these default parameter values, tasks provide async methods that create parameters objects, initialized with these service-specific values. You can then make any changes to the parameter values, before using to execute an operation. Creating parameter objects in this way can be especially useful for operations with many options, such as solving a route.

The following QML code declares a RouteTask. When it is loaded, the RouteTask gets the default parameters from the service. Also, there is code connected to the solveRouteStatusChanged signal which appends a graphic for the route polyline to the graphics overlay, and places the directions text into a list model.

// Create a RouteTask pointing to an online service
   RouteTask {
       id: routeTask
       url: "http://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/Route"

       // Request default parameters once the task is loaded
       onLoadStatusChanged: {
           if (loadStatus === Enums.LoadStatusLoaded) {

       // Store the resulting route parameters
       onCreateDefaultParametersStatusChanged: {
           if (createDefaultParametersStatus === Enums.TaskStatusCompleted) {
               routeParameters = createDefaultParametersResult;

       // Handle the solveRouteStatusChanged signal
       onSolveRouteStatusChanged: {
           if (solveRouteStatus === Enums.TaskStatusCompleted) {
               // Add the route graphic once the solve completes
               var generatedRoute = solveRouteResult.routes[0];
               var routeGraphic = ArcGISRuntimeEnvironment.createObject("Graphic", {geometry: generatedRoute.routeGeometry});

               // set the direction maneuver list model
               directionListModel = generatedRoute.directionManeuvers;

               // hide the solve button and show the direction button
               solveButton.visible = false;
See the QML sample FindRoute for a complete code example.

Alternatively, some parameters objects have constructors that you can use if you know the values of all the input parameters you want to use. This can be more efficient where parameter settings are simple.

Work online or offline

Many tasks can work either online by using services, or offline by using local data and resources. For example, you can geocode an address by using the default Esri geocoding service, your own geocoding service, a locator file (.loc) or a mobile map package (.mmpk).

Earlier in this topic, we saw an example that created a LocatorTask from a URL to a geocoding service. Another option is to declare a LocatorTask from a offline locator stored on the device.

LocatorTask {
       id: locatorTask
       url: dataPath + "/Locators/SanDiegoStreetAddress/SanDiego_StreetAddress.loc"
A third option is to extract a locator task from a mobile map package.
currentLocatorTask = mobileMapList[selectedMmpkIndex].locatorTask;

Tasks and jobs

Some tasks expose operations that have multiple stages (like preparing and downloading a geodatabase), and can generate multiple progress messages (such as percentage complete). These types of tasks are always bound to ArcGIS Server or Local Server. An example is generateGeodatabase on GeodatabaseSyncTask.

Instead of returning results directly, these tasks make use of jobs to monitor status, return progress updates, and return their results. Each Job represents a specific operation of a task. Jobs are useful for longer-running operations, because they can also be paused, resumed, and cancelled. Your app can support a user action or host OS terminating a running job object, and then re-create and resume the job later.

To use operations like these:

  1. Create the task by initializing it to use the required data or service.
  2. Define the input parameters for the task.
  3. Call the async operation method to get a job, passing in the input parameters you defined.
  4. Start the job.
  5. Optionally, listen for changes to the job status and check the job messages, for example to update a UI and report progress to the user.
  6. Listen for the job completion and get the results from the operation. Check for errors in the job, and if successful, use the results.

ExportTileCacheTask {
       id: exportTask
       url: tiledServiceUrl
       property var exportJob

       onCreateDefaultExportTileCacheParametersStatusChanged: {
           if (createDefaultExportTileCacheParametersStatus === Enums.TaskStatusCompleted) {
               params = defaultExportTileCacheParameters;

               // export the cache with the parameters

       function generateDefaultParameters() {
           // generate the default parameters with the extent and map scales specified
           exportTask.createDefaultExportTileCacheParameters(tileCacheExtent, mapView.mapScale, tiledLayer.maxScale);

       function executeExportTileCacheTask(params) {
           // execute the asynchronous task and obtain the job
           exportJob = exportTask.exportTileCache(params, outputTileCache);

           // check if job is valid
           if (exportJob) {
               // show the export window
               exportWindow.visible = true;

               // connect to the job's status changed signal to know once it is done

           } else {
               exportWindow.visible = true;
               statusText = "Export failed";

       function updateJobStatus() {
           switch(exportJob.jobStatus) {
           case Enums.JobStatusFailed:
               statusText = "Export failed";
           case Enums.JobStatusNotStarted:
               statusText = "Job not started";
           case Enums.JobStatusPaused:
               statusText = "Job paused";
           case Enums.JobStatusStarted:
               console.log("In progress...");
               statusText = "In progress...";
           case Enums.JobStatusSucceeded:
               statusText = "Adding TPK...";

       function displayOutputTileCache(tileCache) {
           // create a new tiled layer from the output tile cache
           var tiledLayer = ArcGISRuntimeEnvironment.createObject("ArcGISTiledLayer", { tileCache: tileCache } );

           // create a new basemap with the tiled layer
           var basemap = ArcGISRuntimeEnvironment.createObject("Basemap");

           // set the new basemap on the map
           map.basemap = basemap;

           // zoom to the new layer and hide window once loaded
           tiledLayer.loadStatusChanged.connect(function() {
               if (tiledLayer.loadStatus === Enums.LoadStatusLoaded) {
                   extentRectangle.visible = false;
                   downloadButton.visible = false;
                   mapView.setViewpointScale(mapView.mapScale * .5);

       Component.onDestruction: {

Calling Job.jobStatus retrieves the current point in the job's workflow. Started jobs periodically receive job changed events; this happens with decreasingly frequency as a job progresses; more than one JobMessage may have been added to the job for each call. The job done listener is called as soon as the job is complete, for both successes and failures. Completed jobs, whether they have succeeded or failed, cannot be restarted.

Report job progress

A job represents an asynchronously running operation that might take some time to finish. As described previously, you can monitor changes to job status for notification when a job has completed, failed, or been cancelled, but what about the time in-between? Users may become frustrated waiting for a long job to complete without getting feedback on it's progress. Fortunately, jobs provide a mechanism for reporting the current progress (percentage complete) for the running operation they represent.

As the job runs it . You can get the current progress of the job from the job's Progress property, an integer representing the percentage of the operation that has been completed. This allows your app to provide more specific information about the status of a running job using UI elements like progress bars, for example.

Pause, resume, or cancel job

Jobs are designed to effectively handle a user exiting an app while the job is running, and to handle the app being terminated by the host operating system. Jobs also deal with explicit pausing and cancellation of the operation.

Cancel a job

Sometimes the results of a job are no longer required. For example, a user could change their mind about the area of a tile cache they want to download and want to cancel the job and start over.

Calling Job.cancel on a job immediately changes its status to JobStatusFailed and adds additional information to the job error object. The job completion listener will be called. The error object indicates the error domain and code, which allows you to identify when cancellation has occurred.

Cancelled jobs cannot be restarted, although you can start a new job by re-using the same parameters. Cancelling a job does not necessarily stop any server-side processes, because not all services support cancellation on the server side.

Pause and resume a job

Jobs can be long running operations, so there is no guarantee that they will be completed while the app is running. You can pause a job explicitly using Job.pause. For example when an app is backgrounded and does not have permissions for background operation. Pausing may also be useful if a user wishes to temporarily stop network access for any reason.

Job changed messages will not be received for a paused job. Pausing a job does not stop any server-side processes from executing. While a job is paused, outstanding requests can complete, and therefore it's possible that a resuming a job will result in it having a different state to when it was paused.

You can serialize a job to JSON to persist it if your app is backgrounded, or the process is otherwise terminated. When you deserialize it again, the job will be in the AGSJobStatusPaused state regardless of its state when serialized, and should be restarted to resume listening for completion. The job changed listener can be a good place to update the job JSON for storage by your app.

Loss of network connection

Additionally, jobs using services are designed to handle situations where network connectivity is temporarily lost without needing to be immediately paused. A started job will ignore errors such as network errors for a period of up to 10 minutes. If errors continue for longer, the job will fail and the message will indicate the loss of network connection.

To deal with partially-connected workflows, you can serialize and pause a job when your app detects that network connectivity has been lost for a few minutes to avoid job failure purely due to this lack of network connectivity (as failed jobs cannot be restarted). The job can then be deserialized and resumed when connectivity is reinstated.