diff --git a/features.batch.inc b/features.batch.inc
new file mode 100644
index 0000000..dbb5f63
--- /dev/null
+++ b/features.batch.inc
@@ -0,0 +1,99 @@
+<?php
+
+/**
+ * @file
+ * Contains batch operation for enabling, disabling, and rebuilding features
+ * components.
+ */
+
+/**
+ * Build a features component batch from an array of modules.
+ *
+ * @param $op
+ *   Action to perform. Valid actions are 'enable' and 'disable'.
+ * @param $modules
+ *   An array of modules that were just enabled or disabled
+ *
+ * @return
+ * A batch structure.
+ */
+function _features_restore_batch_set($op, $modules) {
+  $operations = array();
+
+  $components = features_get_components();
+
+  // Go through all modules and gather features.
+  foreach ($modules as $module) {
+    if ($feature = features_load_feature($module)) {
+      // Retain the order of component types, which is needed for example to ensure
+      // that user roles are created ahead of user permissions that reference them.
+      $feature_components = array_keys(array_intersect_key($components, $feature->info['features']));
+      $operations[] = array('_features_restore_batch', array($op, $module, $feature_components));
+    }
+  }
+
+  if (!empty($operations)) {
+    $batch = array(
+      'operations' => $operations,
+      'finished' => '_features_restore_batch_finished',
+      'title' => t('Processing feature components'),
+      'init_message' => t('Starting processing'),
+      'error_message' => t('Error processing feature components'),
+      'file' => drupal_get_path('module', 'features') . '/features.batch.inc',
+    );
+    // If we use a single multi-pass operation, the default
+    // 'Remaining x of y operations' message will be confusing here.
+    if (count($operations) == 1) {
+      $batch['progress_message'] = '';
+    }
+    batch_set($batch);
+    batch_process();
+  }
+}
+
+/**
+ * Perform feature component enabling or disabling and rebuilding as a batch
+ * step.
+ *
+ * @param $op
+ *   Action to perform. Valid actions are 'enable' and 'disable'.
+ * @param $module
+ *   The machine name of the features module providing the components.
+ * @param $components
+ *   An array of components to enable or disable.
+ * @param $context
+ *   Contextual information on the current batch operation.
+ */
+function _features_restore_batch($op, $module, $components, &$context) {
+  // Get working values.
+  if (!isset($context['sandbox']['max'])) {
+    // First iteration: initialize working values.
+    // Process components by groups of 5.
+    $context['sandbox']['components'] = array_chunk($components, 5);
+    $context['sandbox']['progress'] = 0;
+    $context['sandbox']['max'] = count($components);
+  }
+
+  $batch_components = array_shift($context['sandbox']['components']);
+
+  _features_restore($op, array($module => $batch_components));
+  _features_restore('rebuild', array($module => $batch_components));
+
+  $context['sandbox']['progress'] += count($batch_components);
+
+  // Inform the batch engine that we are not finished,
+  // and provide an estimation of the completion level we reached.
+  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+  }
+}
+
+/**
+ * Finished callback of feature enable batch.
+ *
+ * Rebuild the list of features includes and flush caches.
+ */
+function _features_restore_batch_finished($success, $results) {
+  features_include(TRUE);
+  drupal_flush_all_caches();
+}
diff --git a/features.module b/features.module
index b2c6dfa..90d4bc0 100644
--- a/features.module
+++ b/features.module
@@ -291,38 +291,16 @@ function features_help($path, $arg) {
  * Implements hook_modules_disabled().
  */
 function features_modules_disabled($modules) {
-  // Go through all modules and gather features that can be disabled.
-  $items = array();
-  foreach ($modules as $module) {
-    if ($feature = features_load_feature($module)) {
-      $items[$module] = array_keys($feature->info['features']);
-    }
-  }
-
-  if (!empty($items)) {
-    _features_restore('disable', $items);
-    // Rebuild the list of features includes.
-    features_include(TRUE);
-  }
+  module_load_include('inc', 'features', 'features.batch');
+  _features_restore_batch_set('disable', $modules);
 }
 
 /**
  * Implements hook_modules_enabled().
  */
 function features_modules_enabled($modules) {
-  // Go through all modules and gather features that can be enabled.
-  $items = array();
-  foreach ($modules as $module) {
-    if ($feature = features_load_feature($module)) {
-      $items[$module] = array_keys($feature->info['features']);
-    }
-  }
-
-  if (!empty($items)) {
-    _features_restore('enable', $items);
-    // Rebuild the list of features includes.
-    features_include(TRUE);
-  }
+  module_load_include('inc', 'features', 'features.batch');
+  _features_restore_batch_set('enable', $modules);
 }
 
 /**
