docs: function(progressMeter, traces) {
        if (this.importing_)
          throw new Error('Already importing.');
        this.importing_ = true;
  
        // Just some simple setup. It is useful to have a no-op first
        // task so that we can set up the lastTask = lastTask.after()
        // pattern that follows.
        var importTask = new tr.b.Task(function prepareImport() {
          progressMeter.update('I will now import your traces for you...');
        }, this);
        var lastTask = importTask;
  
        var importers = [];
  
        lastTask = lastTask.after(function createImports() {
          // Copy the traces array, we may mutate it.
          traces = traces.slice(0);
          progressMeter.update('Creating importers...');
          // Figure out which importers to use.
          for (var i = 0; i < traces.length; ++i)
            importers.push(this.createImporter_(traces[i]));
  
          // Some traces have other traces inside them. Before doing the full
          // import, ask the importer if it has any subtraces, and if so, create
          // importers for them, also.
          for (var i = 0; i < importers.length; i++) {
            var subtraces = importers[i].extractSubtraces();
            for (var j = 0; j < subtraces.length; j++) {
              try {
                traces.push(subtraces[j]);
                importers.push(this.createImporter_(subtraces[j]));
              } catch (error) {
                // TODO(kphanee): Log the subtrace file which has failed.
                console.warn(error.name + ': ' + error.message);
                continue;
              }
            }
          }
  
          if (traces.length && !this.hasEventDataDecoder_(importers)) {
            throw new Error(
                'Could not find an importer for the provided eventData.');
          }
  
          // Sort them on priority. This ensures importing happens in a
          // predictable order, e.g. ftrace_importer before
          // trace_event_importer.
          importers.sort(function(x, y) {
            return x.importPriority - y.importPriority;
          });
        }, this);
  
        // Run the import.
        lastTask = lastTask.after(function runImport(task) {
          importers.forEach(function(importer, index) {
            task.subTask(function runImportEventsOnOneImporter() {
              progressMeter.update(
                  'Importing ' + (index + 1) + ' of ' + importers.length);
              importer.importEvents();
            }, this);
          }, this);
        }, this);
  
        // Run the cusomizeModelCallback if needed.
        if (this.importOptions_.customizeModelCallback) {
          lastTask = lastTask.after(function runCustomizeCallbacks(task) {
            this.importOptions_.customizeModelCallback(this.model_);
          }, this);
        }
  
        // Import sample data.
        lastTask = lastTask.after(function(task) {
          importers.forEach(function importSampleData(importer, index) {
            progressMeter.update(
                'Importing sample data ' + (index + 1) + '/' + importers.length);
            importer.importSampleData();
          }, this);
        }, this);
  
        // Autoclose open slices and create subSlices.
        lastTask = lastTask.after(function runAutoclosers() {
          progressMeter.update('Autoclosing open slices...');
          this.model_.autoCloseOpenSlices();
          this.model_.createSubSlices();
        }, this);
  
        // Finalize import.
        lastTask = lastTask.after(function finalizeImport(task) {
          importers.forEach(function(importer, index) {
            progressMeter.update(
                'Finalizing import ' + (index + 1) + '/' + importers.length);
            importer.finalizeImport();
          }, this);
        }, this);
  
        // Run preinit.
        lastTask = lastTask.after(function runPreinits() {
          progressMeter.update('Initializing objects (step 1/2)...');
          this.model_.preInitializeObjects();
        }, this);
  
        // Prune empty containers.
        if (this.importOptions_.pruneEmptyContainers) {
          lastTask = lastTask.after(function runPruneEmptyContainers() {
            progressMeter.update('Pruning empty containers...');
            this.model_.pruneEmptyContainers();
          }, this);
        }
  
        // Merge kernel and userland slices on each thread.
        lastTask = lastTask.after(function runMergeKernelWithuserland() {
          progressMeter.update('Merging kernel with userland...');
          this.model_.mergeKernelWithUserland();
        }, this);
  
        // Create auditors
        var auditors = [];
        lastTask = lastTask.after(function createAuditorsAndRunAnnotate() {
          progressMeter.update('Adding arbitrary data to model...');
          auditors = this.importOptions_.auditorConstructors.map(
            function(auditorConstructor) {
              return new auditorConstructor(this.model_);
            }, this);
          auditors.forEach(function(auditor) {
            auditor.runAnnotate();
            auditor.installUserFriendlyCategoryDriverIfNeeded();
          });
        }, this);
  
        lastTask = lastTask.after(function computeWorldBounds() {
          progressMeter.update('Computing final world bounds...');
          this.model_.computeWorldBounds(this.importOptions_.shiftWorldToZero);
        }, this);
  
        // Build the flow event interval tree.
        lastTask = lastTask.after(function buildFlowEventIntervalTree() {
          progressMeter.update('Building flow event map...');
          this.model_.buildFlowEventIntervalTree();
        }, this);
  
        // Join refs.
        lastTask = lastTask.after(function joinRefs() {
          progressMeter.update('Joining object refs...');
          for (var i = 0; i < importers.length; i++)
            importers[i].joinRefs();
        }, this);
  
        // Delete any undeleted objects.
        lastTask = lastTask.after(function cleanupUndeletedObjects() {
          progressMeter.update('Cleaning up undeleted objects...');
          this.model_.cleanupUndeletedObjects();
        }, this);
  
        // Sort global and process memory dumps.
        lastTask = lastTask.after(function sortMemoryDumps() {
          progressMeter.update('Sorting memory dumps...');
          this.model_.sortMemoryDumps();
        }, this);
  
        // Calculate memory dump graph attributes.
        lastTask = lastTask.after(function calculateMemoryGraphAttributes() {
          progressMeter.update('Calculating memory dump graph attributes...');
          this.model_.calculateMemoryGraphAttributes();
        }, this);
  
        // Run initializers.
        lastTask = lastTask.after(function initializeObjects() {
          progressMeter.update('Initializing objects (step 2/2)...');
          this.model_.initializeObjects();
        }, this);
  
        // Build event indices mapping from an event id to all flow events.
        lastTask = lastTask.after(function buildEventIndices() {
          progressMeter.update('Building event indices...');
          this.model_.buildEventIndices();
        }, this);
  
        // Run audits.
        lastTask = lastTask.after(function runAudits() {
          progressMeter.update('Running auditors...');
          auditors.forEach(function(auditor) {
            auditor.runAudit();
          });
        }, this);
  
        lastTask = lastTask.after(function sortInteractionRecords() {
          progressMeter.update('Updating interaction records...');
          this.model_.sortInteractionRecords();
        }, this);
  
        lastTask = lastTask.after(function sortAlerts() {
          progressMeter.update('Updating alerts...');
          this.model_.sortAlerts();
        }, this);
  
        lastTask = lastTask.after(function lastUpdateBounds() {
          progressMeter.update('Update bounds...');
          this.model_.updateBounds();
        }, this);
  
        lastTask = lastTask.after(function addModelWarnings() {
          progressMeter.update('Looking for warnings...');
          // Log an import warning if the clock is low resolution.
          if (!this.model_.isTimeHighResolution) {
            this.model_.importWarning({
              type: 'low_resolution_timer',
              message: 'Trace time is low resolution, trace may be unusable.',
              showToUser: true
            });
          }
        }, this);
  
        // Cleanup.
        lastTask.after(function() {
          this.importing_ = false;
        }, this);
        return importTask;
      }