¿Qué es la JS sin cabeza en React Native?

No tiene cabeza JS es una característica nativa de reacción integrada que le permite ejecutar código JavaScript en el fondo incluso cuando la interfaz de usuario de la aplicación no está activa. A diferencia de la ejecución del primer plano, donde la aplicación es visible e interactiva, Headless JS ejecuta tareas como procesos separados que no dependen del hilo principal de interfaz de usuario. Esto es esencial para las operaciones que deben continuar después de que el usuario cierre la aplicación o antes de que la abra – tales como la lógica de sincronización de datos de la comunicación

Casos de uso común para la JS sin cabeza

Comprender cuándo utilizar Headless JS le ayuda a decidir si se ajusta a su proyecto.

  • Sincronización de datos – Subir registros o descargar nuevos contenidos cuando la aplicación esté en el fondo.
  • Manejo de notificación de descarga – Realizar lógica personalizada cuando llega una notificación (por ejemplo, actualizar una placa, almacenar datos localmente) antes de mostrar la alerta.
  • Actualizaciones de localización] – Contaminar coordenadas GPS en el fondo para el seguimiento geofencing o en tiempo real.
  • Tareas basadas en el tiempo – Ejecutar un trabajo a intervalos programados (como limpieza diaria) incluso si el usuario no ha abierto la aplicación.
  • Medidas rápidas desde la pantalla de inicio – Realizar una operación corta cuando el usuario presiona una acción 3D Touch o widget.

La JS sin cabeza no está diseñada para computaciones largas o pesadas – el sistema puede matar su proceso de fondo si tarda demasiado tiempo. Para trabajos de fondo prolongados, combine con servicios nativos como WorkManager] (Android) o VolarTasks] (iOS).

Configuración de la JS sin cabeza en React Native: Step‐by‐Step

Implementar la JS sin cabeza implica dos partes principales: crear un controlador JavaScript y registrarlo en el código nativo para cada plataforma. Caminaremos a través de ambas partes.

1. Crear el Handler de Antecedentes de JavaScript

Comience por crear un nuevo archivo, por ejemplo . Este archivo contendrá la función que se ejecuta cuando se activa la tarea de fondo. La función recibe un objeto , que puede contener parámetros pasados del lado nativo.

// BackgroundTask.js
import { AppRegistry } from 'react-native';

const backgroundTask = async (taskData) => {
 console.log('Background task started with data:', taskData);
 // Your background logic goes here
 // For example, fetch new data from an API
 try {
 const response = await fetch('https://api.example.com/sync');
 const data = await response.json();
 // Store data in AsyncStorage or a local database
 console.log('Background sync complete');
 } catch (error) {
 console.error('Background task failed:', error);
 // Optionally throw an error to let the system know
 }
};

AppRegistry.registerHeadlessTask('BackgroundTask', () => backgroundTask);

Tenga en cuenta que el manejador debe ser una función que devuelve una promesa (o una función asinc). El sistema esperará a que la promesa de resolver antes de considerar la tarea completa. Si la Promesa rechaza, el lado nativo puede tratarla como un fracaso.

2. Registrar la tarea en el código nativo Android

Android requiere que usted registre la tarea sin cabeza en su (o ] dependiendo de la versión nativa de React). Además, usted necesita un IngenuoJsTaskService para gestionar el ciclo de vida de tarea.

Paso A – Actualizar MainApplication.java

// android/app/src/main/java/com/yourapp/MainApplication.java
import com.facebook.react.HeadlessJsTaskService;

public class MainApplication extends Application implements ReactApplication {
 // ... existing code

 @Override
 public void onCreate() {
 super.onCreate();
 // Initialize ReactNative instance as usual
 // The task will be registered via AppRegistry in JavaScript
 }
}

No se necesita registro explícito en Java para la tarea en sí misma si se utiliza – React Native automáticamente mapea el controlador JavaScript a una tarea nativa. Sin embargo, debe crear un servicio que puede ser iniciado por el sistema.

Paso B – Crear un servicio de HeadlessJsTask

Crear una nueva clase Java en el mismo paquete:

// android/app/src/main/java/com/yourapp/BackgroundTaskService.java
package com.yourapp;

import android.content.Intent;
import android.os.Bundle;
import com.facebook.react.HeadlessJsTaskService;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.jstasks.HeadlessJsTaskConfig;

public class BackgroundTaskService extends HeadlessJsTaskService {
 @Override
 protected HeadlessJsTaskConfig getTaskConfig(Intent intent) {
 Bundle extras = intent.getExtras();
 if (extras != null) {
 return new HeadlessJsTaskConfig(
 "BackgroundTask",
 Arguments.fromBundle(extras),
 5000, // timeout in ms
 true // allows task to run in foreground as well
 );
 }
 return null;
 }
}

Este servicio extrae cualquier extra de la Intent (como datos de una notificación) y los pasa al controlador JavaScript. El tiempo de salida evita las tareas de ejecución.

Paso C – Añada el Servicio a AndroidManifest.xml

<service android:name=".BackgroundTaskService" />

Ahora cualquier módulo nativo puede iniciar este servicio usando una Intención, o puede utilizar un receptor de radiodifusión para activarlo desde una notificación de empuje.

3. Configurar capacidades de fondo iOS

iOS es más restrictivo sobre la ejecución de fondo. Para utilizar Headless JS en iOS, debe habilitar modos de fondo apropiados y utilizar la programación nativa (como notificaciones de empuje o búsqueda de fondo).

Paso A – Permitir modos de fondo

Abra su proyecto Xcode, vaya a ]Signing & Capabilities] y agregue la capacidad Modos de fondo]. Compruebe los modos que necesita su aplicación, por ejemplo, “Remotear el embrague” o “Remote notifications”.

Paso B – Implementar los métodos de delegados de aplicaciones

En , implementar los métodos delegados necesarios para recibir eventos de fondo y comenzar la tarea sin cabeza.

// AppDelegate.m (Objective-C)
#import <React/RCTAppDelegate.h>
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTRootView.h>

@implementation AppDelegate

- (void)application:(UIApplication *)application
 performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
 // You can get the bridge from your ReactNative root view
 RCTBridge *bridge = [self.rootView bridge];
 [bridge.eventDispatcher sendAppEventWithName:@"BackgroundFetch"
 body:@{@"taskName": @"BackgroundTask"}];
 completionHandler(UIBackgroundFetchResultNewData);
}

@end

En el lado JavaScript, necesitas escuchar el evento y luego iniciar la tarea sin cabeza manualmente. Sin embargo, Headless JS on iOS es más comúnmente utilizado junto con notificaciones de empuje – cuando llega una notificación, puedes ejecutar código sin cabeza antes de mostrar la alerta.

Para las notificaciones de empuje, implemente en AppDelegate y envíe un evento para Reactar Nativo.

Tareas de fondo desencadenantes

Una vez establecido, puede activar una tarea de la JS sin cabeza de varias fuentes:

  • Notificaciones de archivo] (Android & iOS) – Utilice un receptor de transmisión personalizado en Android o el delegado de la aplicación en iOS para iniciar el servicio cuando llegue una notificación silenciosa.
  • Módulos nativos] – Crear un módulo nativo que llame a en Android o publique una notificación en iOS.
  • Recoge de fondo (iOS) – iOS llama periódicamente a tu delegado de aplicaciones; puedes aprovechar esa oportunidad para despedir la tarea sin cabeza.
  • Alarmas ] – Use o para programar la ejecución periódica.

Ejemplo: Inicio del servicio Android de un receptor de radio que escucha una acción personalizada:

public class AlarmReceiver extends BroadcastReceiver {
 @Override
 public void onReceive(Context context, Intent intent) {
 Intent serviceIntent = new Intent(context, BackgroundTaskService.class);
 serviceIntent.putExtra("source", "alarm");
 context.startService(serviceIntent);
 }
}

Luego registre el receptor en AndroidManifest.xml y programe una alarma usando .

Buenas prácticas para la operación de fondo fiable

Las tareas de fondo son inherentemente frágiles – el sistema operativo puede matar su proceso en cualquier momento. Siga estas directrices para hacer sus tareas de la JS sin cabeza robustas:

  • Mantén cortos las tareas. Apunta por menos de 30 segundos de ejecución. Usar los plazos (los 5000ms en nuestro ejemplo) para prevenir las tareas colgantes.
  • Errores de fuga con gracia. Envuelve tu lógica en el intento/encuentre. Si la tarea falla, considera registrarte localmente utilizando un reportero de fallo (como Sentry) y no bloquees la interfaz de usuario.
  • Utilizar patrones fuera de línea. Como las tareas de fondo suelen funcionar sin garantías de red, implementar retries y almacenar operaciones fallidas para más adelante.
  • Evitar la gestión de estado pesado. No depender de Redux o estado de reacción – las tareas de fondo se ejecutan en un contexto JavaScript separado. Use AsyncStorage, SQLite (react-native-sqlite-storage), o almacenamiento seguro para la persistencia de datos.
  • Prueba en dispositivos reales. El emulador Android y el simulador iOS se comportan de forma diferente del hardware físico. Prueba con pantalla de dispositivo apagada y en modo Doze (Android) o modo de potencia baja (iOS).
  • Optimización de baterías de retrospecto. Usa con un bloqueo de vela en Android para evitar que la CPU se duerma, pero libera la cerradura rápidamente. En iOS, los intervalos de captura de fondo son gestionados por el sistema: respeta el tiempo concedido.

Debugging Headless JS Tasks

Debugging código de fondo es complicado porque no puede utilizar el inspector de DevTools Chrome cuando la interfaz de usuario no está activa.

  • Logcat (Android) – Añada declaraciones; aparecen en el logcat con la etiqueta “ReactNativeJS”.
  • Registros de archivos locales] – Escribe entradas de registro a un archivo en el dispositivo y recuperáralas más tarde.
  • Flipper – React Native Flipper puede capturar los registros de consola de tareas sin cabeza (asegurar que establezca a verdad en su plugin Flipper).
  • Native logging] – Use en su servicio Java para rastrear cuando la tarea comience y termine.

Limitaciones y consideraciones

La JS sin cabeza no es una bala de plata. Comprende sus limitaciones para evitar sorpresas:

  • No interacción de la UI. No se puede dar vistas ni mostrar alertas dentro de una tarea sin cabeza. Utilice notificaciones de presión para informar al usuario de los resultados.
  • ]Tarea única por proceso. Reactúa El servicio sin cabeza de Native funciona sólo una tarea JavaScript a la vez. Si necesita múltiples operaciones de fondo paralelo, considere el uso de servicios nativos (WorkManager en Android, BGTaskScheduler en iOS).
  • iOS restricciones. iOS prospera mucho la ejecución de fondo. La JS sin cabeza en iOS es sólo confiable para tareas cortas desencadenadas por notificaciones de empuje o búsqueda de fondo – el sistema puede retrasar o prevenir la ejecución si la fuerza de usuario se encarga de la aplicación.
  • No Redux store. Como se mencionó anteriormente, el contexto JavaScript sin cabeza no comparte la misma tienda Redux como la aplicación principal. Utilice almacenamiento persistente para pasar datos entre los dos contextos.
  • Conclusión de la aplicación. Si la fuerza de usuario cierra su aplicación en Android 12+ o iOS, las tareas de fondo pueden no funcionar hasta que el usuario abra la aplicación de nuevo.

Comparación con otras bibliotecas de fondo

React Native’s Headless JS es el mecanismo integrado de menor nivel. Para muchos casos de uso, los paquetes comunitarios proporcionan una API más conveniente:

LibraryPlatformFeatures
react-native-background-fetchAndroid & iOSPeriodic background fetching with system‑managed scheduling; supports Headless JS internally.
react-native-background-actionsAndroid & iOSRuns a long‑running foreground service (with persistent notification) – can execute heavy tasks.
WorkManager (Android only via native bridge)AndroidDeferrable, reliable background work with constraints (network, battery); does not use Headless JS by default.
Headless JS (vanilla)Android & iOSSimple, built‑in, no extra dependencies; best for short tasks triggered by push or alarms.

Si su tarea debe funcionar incluso después de un reinicio, o necesita una programación compleja, considere la posibilidad de envolver una implementación nativa WorkManager y exponerla a través de un módulo nativo de React Native. Headless JS sigue siendo excelente para trabajos ligeros “fuego y perget”.

Ejemplo completo: sincronización de datos en la notificación de empuje

Pongamos todo junto con un escenario realista: cuando una notificación de empuje silenciosa llega a Android, la aplicación descarga los últimos datos de una API y lo almacena localmente. El usuario nunca ve un cambio de interfaz de usuario – los datos están listos cuando abren la aplicación.

Android – Receptor de empuje

Crear un receptor de radio que escuche la acción de empuje (por ejemplo, desde FCM):

public class PushReceiver extends BroadcastReceiver {
 @Override
 public void onReceive(Context context, Intent intent) {
 Intent serviceIntent = new Intent(context, BackgroundTaskService.class);
 serviceIntent.putExtra("action", "syncData");
 context.startService(serviceIntent);
 }
}

Regístrelo en AndroidManifest.xml con un filtro de intención para su acción de empuje (o use FirebaseMessagingService's para iniciar el servicio directamente).

Empleador de JavaScript

// BackgroundTask.js (expanded)
import { AppRegistry } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';

const backgroundTask = async (taskData) => {
 const { action } = taskData;
 if (action === 'syncData') {
 try {
 const response = await fetch('https://api.example.com/latest-data');
 const json = await response.json();
 await AsyncStorage.setItem('cachedData', JSON.stringify(json));
 console.log('Data synced successfully');
 } catch (error) {
 // Write error to a local log
 const errorLog = await AsyncStorage.getItem('errorLog');
 const newLog = (errorLog ? errorLog + '\n' : '') + new Date().toISOString() + ': ' + error.message;
 await AsyncStorage.setItem('errorLog', newLog);
 }
 }
};

AppRegistry.registerHeadlessTask('BackgroundTask', () => backgroundTask);

Cuando el usuario reabsorbe la aplicación, el primer plano puede leer y actualizar la interfaz de usuario inmediatamente, creando una experiencia sin fisuras en línea.

Rendimiento y impacto de batería

Las tareas sin cabeza funcionan en el mismo motor JavaScript que la aplicación principal, pero en un contexto separado. Cada tarea gira en una nueva instancia JS sin cabeza, que consume memoria y CPU. Para minimizar el drenaje de la batería:

  • Utilice el tiempo más corto que todavía permite que el trabajo se complete.
  • No realice encuestas frecuentes – prefiera los desencadenantes basados en el empuje.
  • En Android, llame del servicio después de que la tarea termine (HeadlessJsTaskService hace esto automáticamente cuando la Promesa resuelve).
  • Combina con APIs de batería como WorkManager para Android y BGTaskScheduler para iOS si necesitas trabajo periódico.

Conclusión

JS es una herramienta poderosa en el cinturón de desarrollo nativo de React para manejar tareas de fondo cortas que deben ejecutar independientemente de la visibilidad de la aplicación. Al configurar un controlador JavaScript y conectarlo con los desencadenantes nativos de fondo – tales como notificaciones de empuje, alarmas o cofre de fondo – puede crear aplicaciones sensibles y basadas en datos que se sientan siempre a la vista.

Para más lectura, consulte la documentación oficial de React Native en [Indefinido JS (Android)] y Indefinido JS (iOS). También compruebe la biblioteca de retratar-retrato-retrato-retrato-retrospectiva] para la gestión periódica de tareas.