Creación de aplicaciones híbridas con ChakraCore

 

 

 

  • Advertise on Smashing Magazine
  • Register!

  • Índice
    1. Hola, ChakraCore.
    2. Creación de aplicaciones con ChakraCore
    3. Tipos de valores de JavaScript en ChakraCore
      1. Conversión de cadenas con serialización y análisis
      2. Conversión de modelo de objetos directos (con Json.NET)
    4. Requisitos de enhebrado de ChakraCore
      1. Opciones de cola de subprocesos
      2. Captura de subprocesos de ThreadPool
      3. Programador de tareas
    5. Consideraciones sobre el tiempo de ejecución de ChakraCore
      1. Recolección de basura
      2. Conclusión: ¿JIT o no JIT?
      3. Más prácticas con aplicaciones web y móviles
      4. Otras lecturas

    La razón clave para la investigación de ChakraCore por parte de Eric Rozell fue admitir el marco React Native en la Plataforma universal de Windows, que es un marco para declarar aplicaciones que utilizan JavaScript y el modelo de programación React. Integrar ChakraCore en una aplicación C# es bastante fácil y en este artículo, Eric Rozell le mostrará cómo.

     

    Hay muchas razones para incorporar capacidades de JavaScript en una aplicación. Un ejemplo puede ser tomar una dependencia de una biblioteca de JavaScript que aún no se ha adaptado al lenguaje en el que está desarrollando. Otra razón podría ser su deseo de permitir a los usuarios pequeñas evalrutinas o funciones en JavaScript, por ejemplo, en aplicaciones de procesamiento de datos. .

    ChakraCore proporciona un motor JavaScript de alto rendimiento que impulsa el navegador Microsft Edge y las aplicaciones de Windows escritas con WinJS . La razón clave de nuestra investigación de ChakraCore fue admitir el marco React Native en la Plataforma universal de Windows, un marco para declarar aplicaciones que utilizan JavaScript y el modelo de programación React .

    Hola, ChakraCore.

    Incrustar ChakraCore en una aplicación C# es bastante fácil. Para comenzar, obtenga una copia del contenedor de tiempo de ejecución de JavaScript de GitHub . Incluya este código directamente en su proyecto o cree su propia dependencia de biblioteca a partir de él, lo que mejor se adapte a sus necesidades. También hay una aplicación de consola muy sencilla que muestra cómo evaluar el código fuente de JavaScript y convertir valores del tiempo de ejecución de JavaScript en cadenas de C#.

    Creación de aplicaciones con ChakraCore

    Hay algunos pasos adicionales a seguir al crear aplicaciones C# con ChakraCore integrado. Al momento de escribir este artículo, no hay archivos binarios públicos para ChakraCore. Pero no te preocupes. Construir ChakraCore es tan fácil como esto:

    1. Clona el repositorio de ChakraCore Git .
    2. Abra la solución en Visual Studio (se requieren VS 2015 y el SDK de Windows 10 si desea compilar para ARM).
    3. Compile la solución desde Visual Studio.
    4. El resultado de la compilación se colocará en BuildVcBuildbinrelación con su carpeta raíz de Git.

    Si desea compilar desde la línea de comando, abra un símbolo del sistema para desarrolladores para Visual Studio, navegue hasta la carpeta raíz de Git para ChakraCore y ejecute:

    msbuild BuildChakra.Core.sln /p:Configuration=Debug /p:Platform=x86

    Querrá reemplazar los parámetros de Configuración y Plataforma con la configuración adecuada para su compilación.

    Ahora que tiene una versión de ChakraCore.dll, tiene algunas opciones sobre cómo enviarlo con su aplicación. La forma más sencilla es simplemente copiar y pegar el binario en la carpeta de salida de su compilación. Para mayor comodidad, redacté un objetivo de MSBuild simple para incluirlo en su .csproj para copiar automáticamente estos archivos binarios cada vez que cree:

     

    Target Name="AfterBuild" ItemGroup ChakraDependencies Include="$(ReferencesPath)ChakraCore.*" / /ItemGroup Copy SourceFiles="@(ChakraDependencies)" DestinationFolder="$(OutputPath) " //Target

    Para aquellos que no hablan MSBuild, una de las convenciones de MSBuild es ejecutar destinos en su proyecto con nombres AfterBuilddespués de que se complete la compilación. El fragmento de XML anterior se traduce aproximadamente como "una vez completada la compilación, busque en la ruta de referencia archivos que coincidan con el patrón ChakraCore.* y copie esos archivos en el directorio de salida". $(ReferencesPath)También deberá configurar la propiedad en su .csproj.

    Si está creando su aplicación para múltiples plataformas, es útil eliminar las dependencias de ChakraCore.dll en los nombres de las carpetas según la configuración y la plataforma de su compilación. Por ejemplo, considere la siguiente estructura:

    ├── References ├── x86 ├── Debug ├── ChakraCore.dll ├── ChakraCore.pdb ├── Release ├── ... ├── x64 ├── ... ├── ARM ├── ...

    De esa manera puede declarar la propiedad MSBuild $(ReferencesPath)en función de sus propiedades de compilación, por ejemplo

    References$(Configuration)$(Platform)

    Tipos de valores de JavaScript en ChakraCore

    El primer paso para crear aplicaciones más complejas con ChakraCore es comprender el modelo de datos. JavaScript es un lenguaje dinámico y sin tipo que admite funciones de primera clase. El modelo de datos para valores de JavaScript en ChakraCore admite estos diseños. Estos son los tipos de valores admitidos en Chakra:

    • Undefined,
    • Null,
    • Number,
    • String,
    • Boolean,
    • Object,
    • Function,
    • Error,
    • Array.

    Conversión de cadenas con serialización y análisis

    Hay varias formas de reunir datos desde CLR al tiempo de ejecución de JavaScript. Una forma sencilla es analizar y serializar los datos como una cadena JSON una vez que ingresan al tiempo de ejecución, de la siguiente manera:

    var jsonObject = JavaScriptValue.GlobalObject.GetProperty( JavaScriptPropertyId.FromString("JSON")); var stringify = jsonObject.GetProperty( JavaScriptPropertyId.FromString("stringify")) var parse = jsonObject.GetProperty( JavaScriptPropertyId.FromString("parse")); var jsonInput = @"{""foo"":42}"; var stringInput = JavaScriptValue.FromString(jsonInput); var parsedInput = parse.CallFunction(JavaScriptValue.GlobalObject, stringInput); var stringOutput = stringify.CallFunction(JavaScriptValue.GlobalObject, parsedInput); var jsonOutput = stringOutput.ToString();Debug.Assert(jsonInput == jsonOutput);

    En el código anterior, reunimos los datos JSON {“foo”:42}en el tiempo de ejecución como una cadena y analizamos los datos usando la JSON.parsefunción. El resultado es un objeto JavaScript, que usamos como entrada para la JSON.stringifyfunción, luego usamos el ToString()método en el valor del resultado para volver a colocar el resultado en una cadena .NET. Obviamente, la idea sería usar el parsedInputobjeto como entrada para su lógica que se ejecuta en Chakra y aplicar la función stringify solo cuando necesite volver a reunir los datos.

     

    Conversión de modelo de objetos directos (con Json.NET)

    Un enfoque alternativo al enfoque basado en cadenas de la sección anterior sería utilizar las API nativas de Chakra para construir los objetos directamente en el tiempo de ejecución de JavaScript. Si bien puede elegir cualquier modelo de datos JSON que desee para su aplicación C#, elegimos Json.NET debido a su popularidad y características de rendimiento. El resultado básico que buscamos es una función de JavaScriptValue (el modelo de datos de Chakra) a JToken (el modelo de datos Json.NET) y la función inversa de JToken a JavaScriptValue. Dado que JSON es una estructura de datos de árbol, un visitante recursivo es un buen enfoque para implementar los convertidores.

    Aquí está la lógica para la clase de visitante que convierte valores de JavaScriptValue a JToken:

    public sealed class JavaScriptValueToJTokenConverter{ private static readonly JToken s_true = new JValue(true); private static readonly JToken s_false = new JValue(false); private static readonly JToken s_null = JValue.CreateNull(); private static readonly JToken s_undefined = JValue.CreateUndefined(); private static readonly JavaScriptValueToJTokenConverter s_instance = new JavaScriptValueToJTokenConverter(); private JavaScriptValueToJTokenConverter() { } public static JToken Convert(JavaScriptValue value) { return s_instance.Visit(value); } private JToken Visit(JavaScriptValue value) { switch (value.ValueType) { case JavaScriptValueType.Array: return VisitArray(value); case JavaScriptValueType.Boolean: return VisitBoolean(value); case JavaScriptValueType.Null: return VisitNull(value); case JavaScriptValueType.Number: return VisitNumber(value); case JavaScriptValueType.Object: return VisitObject(value); case JavaScriptValueType.String: return VisitString(value); case JavaScriptValueType.Undefined: return VisitUndefined(value); case JavaScriptValueType.Function: case JavaScriptValueType.Error: default: throw new NotSupportedException(); } } private JToken VisitArray(JavaScriptValue value) { var array = new JArray(); var propertyId = JavaScriptPropertyId.FromString("length"); var length = (int)value.GetProperty(propertyId).ToDouble(); for (var i = 0; i length; ++i) { var index = JavaScriptValue.FromInt32(i); var element = value.GetIndexedProperty(index); array.Add(Visit(element)); } return array; } private JToken VisitBoolean(JavaScriptValue value) { return value.ToBoolean() ? s_true : s_false; } private JToken VisitNull(JavaScriptValue value) { return s_null; } private JToken VisitNumber(JavaScriptValue value) { var number = value.ToDouble(); return number % 1 == 0 ? new JValue((long)number) : new JValue(number); } private JToken VisitObject(JavaScriptValue value) { var jsonObject = new JObject(); var properties = Visit(value.GetOwnPropertyNames()).ToObject(); foreach (var property in properties) { var propertyId = JavaScriptPropertyId.FromString(property); var propertyValue = value.GetProperty(propertyId); jsonObject.Add(property, Visit(propertyValue)); } return jsonObject; } private JToken VisitString(JavaScriptValue value) { return JValue.CreateString(value.ToString()); } private JToken VisitUndefined(JavaScriptValue value) { return s_undefined; }}

    Y aquí está la lógica inversa de JToken al valor de JavaScript: Anime en Español

     

    public sealed class JTokenToJavaScriptValueConverter{ private static readonly JTokenToJavaScriptValueConverter s_instance = new JTokenToJavaScriptValueConverter(); private JTokenToJavaScriptValueConverter() { } public static JavaScriptValue Convert(JToken token) { return s_instance.Visit(token); } private JavaScriptValue Visit(JToken token) { if (token == null) throw new ArgumentNullException(nameof(token)); switch (token.Type) { case JTokenType.Array: return VisitArray((JArray)token); case JTokenType.Boolean: return VisitBoolean((JValue)token); case JTokenType.Float: return VisitFloat((JValue)token); case JTokenType.Integer: return VisitInteger((JValue)token); case JTokenType.Null: return VisitNull(token); case JTokenType.Object: return VisitObject((JObject)token); case JTokenType.String: return VisitString((JValue)token); case JTokenType.Undefined: return VisitUndefined(token); default: throw new NotSupportedException(); } } private JavaScriptValue VisitArray(JArray token) { var n = token.Count; var array = AddRef(JavaScriptValue.CreateArray((uint)n)); for (var i = 0; i n; ++i) { var value = Visit(token[i]); array.SetIndexedProperty(JavaScriptValue.FromInt32(i), value); value.Release(); } return array; } private JavaScriptValue VisitBoolean(JValue token) { return token.Value() ? JavaScriptValue.True : JavaScriptValue.False; } private JavaScriptValue VisitFloat(JValue token) { return AddRef(JavaScriptValue.FromDouble(token.Value())); } private JavaScriptValue VisitInteger(JValue token) { return AddRef(JavaScriptValue.FromDouble(token.Value())); } private JavaScriptValue VisitNull(JToken token) { return JavaScriptValue.Null; } private JavaScriptValue VisitObject(JObject token) { var jsonObject = AddRef(JavaScriptValue.CreateObject()); foreach (var entry in token) { var value = Visit(entry.Value); var propertyId = JavaScriptPropertyId.FromString(entry.Key); jsonObject.SetProperty(propertyId, value, true); value.Release(); } return jsonObject; } private JavaScriptValue VisitString(JValue token) { return AddRef(JavaScriptValue.FromString(token.Value())); } private JavaScriptValue VisitUndefined(JToken token) { return JavaScriptValue.Undefined; } private JavaScriptValue AddRef(JavaScriptValue value) { value.AddRef(); return value; }}

    Como ocurre con cualquier algoritmo recursivo, existen casos base y pasos de recursividad. En este caso, los casos base son los "nodos hoja" del árbol JSON (es decir, indefinidos, nulos, números, booleanos, cadenas) y los pasos recursivos ocurren cuando encontramos matrices y objetos.

     

    El objetivo de la conversión del modelo de objetos directos es reducir la presión sobre el recolector de basura, ya que la serialización y el análisis generarán muchas cadenas intermedias. Tenga en cuenta que su elección del modelo de objetos .NET para JSON (Json.NET en los ejemplos anteriores) también puede tener un impacto en su decisión de utilizar el método de conversión del modelo de objetos directo descrito en esta sección o el método de serialización/análisis de cadenas descrito. en el apartado anterior. Si su decisión se basa exclusivamente en el rendimiento y su aplicación no está vinculada a GC, el enfoque de clasificación de cadenas superará la conversión del modelo de objetos directos (especialmente con la sobrecarga de ida y vuelta del código nativo al administrado para árboles JSON grandes). .

    Debe evaluar el impacto en el rendimiento de cualquiera de los enfoques en su escenario antes de elegir uno u otro. Para ayudar en esa investigación, publiqué una herramienta simple para calcular el rendimiento y el impacto de la recolección de basura tanto para CLR como para Chakra en GitHub .

    Requisitos de enhebrado de ChakraCore

    El tiempo de ejecución de ChakraCore es de un solo subproceso en el sentido de que solo un subproceso puede tener acceso a él a la vez. Sin embargo, esto no significa que deba designar un hilo para hacer todo el trabajo en JavaScriptRuntime (aunque puede ser más fácil hacerlo).

    Configurar el tiempo de ejecución de JavaScript es relativamente sencillo:

    var runtime = JavaScriptRuntime.Create();

    Antes de poder utilizar este tiempo de ejecución en cualquier subproceso, primero debe establecer el contexto para un subproceso en particular:

    var context = runtime.CreateContext(); JavaScriptContext.Current = context;

    Cuando haya terminado de usar ese hilo para el trabajo de JavaScript por el momento, asegúrese de restablecer el contexto de JavaScript a un valor no válido:

    JavaScriptContext.Current = JavaScriptContext.Invalid;

    En algún momento posterior, en cualquier otro hilo, simplemente vuelva a crear o reasigne el contexto como se indicó anteriormente. Si intenta asignar el contexto simultáneamente en dos subprocesos diferentes para el mismo tiempo de ejecución, ChakraCore generará una excepción como esta:

    var t1 = Task.Run(() ={ JavaScriptContext.Current = runtime.CreateContext(); Task.Delay(1000).Wait(); JavaScriptContext.Current = JavaScriptContext.Invalid;}); var t2 = Task.Run(() ={ JavaScriptContext.Current = runtime.CreateContext(); Task.Delay(1000).Wait(); JavaScriptContext.Current = JavaScriptContext.Invalid;}); Task.WaitAll(t1, t2);

    Si bien es apropiado generar una excepción, nada debería impedirle utilizar varios subprocesos simultáneamente para dos tiempos de ejecución diferentes. De manera similar, si intenta deshacerse del tiempo de ejecución sin restablecer primero el contexto a un valor no válido, ChakraCore generará una excepción notificando que el tiempo de ejecución está en uso:

     

    using (var runtime = JavaScriptRuntime.Create()){ var context = runtime.CreateContext(); JavaScriptContext.Current = context;}

    Si encuentra la excepción "el tiempo de ejecución está en uso" que surge de eliminar el tiempo de ejecución antes de desarmar el contexto, verifique la actividad del subproceso de JavaScript para detectar cualquier comportamiento asincrónico. La forma en que funciona async/await en C# generalmente permite que cualquier subproceso del grupo de subprocesos lleve a cabo una continuación después de completar una operación asincrónica. Para que ChakraCore funcione correctamente, el contexto debe ser desarmado por exactamente el mismo hilo físico (no el hilo lógico) que lo configuró inicialmente. Para obtener más información, consulte el sitio de Microsoft Developer Network sobre Paralelismo de tareas .

    Opciones de cola de subprocesos

    En nuestra implementación de React Native en Windows, consideramos algunos enfoques diferentes para garantizar que todas las operaciones de JavaScript fueran de un solo subproceso. React Native tiene tres subprocesos principales de actividad: el subproceso de la interfaz de usuario, el subproceso del módulo nativo en segundo plano y el subproceso de JavaScript. Dado que el trabajo de JavaScript puede originarse desde el subproceso del módulo nativo o desde el subproceso de la interfaz de usuario y, en términos generales, cada subproceso no bloquea la espera de que se complete la actividad en ningún otro subproceso, también tenemos el requisito de implementar una cola FIFO para el trabajo de JavaScript.

    Captura de subprocesos de ThreadPool

    Una de las opciones que consideramos fue bloquear permanentemente un subproceso del grupo de subprocesos para evaluar las operaciones de JavaScript. Aquí está el código de muestra para eso:

    // Initializes the thread queuevar queue = new BlockingCollection();var asyncAction = ThreadPool.RunAsync( _ = { JavaScriptContext.Current = context; while (true) { var action = queue.Take(); if (... /* Check disposal */) break; try { action(); } catch (Exception ex) { ... /* Handle exceptions */ } } JavaScriptContext.Current = JavaScriptContext.Invalid; }, WorkItemPriority.Normal);// Enqueues workqueue.Add(() = JavaScriptContext.RunScript(... /* JavaScript */);

    El beneficio de este enfoque es su simplicidad, ya que sabemos que un único hilo ejecuta todas las operaciones de JavaScript. El inconveniente es que estamos bloqueando permanentemente un subproceso del grupo de subprocesos, por lo que no se puede utilizar para otros trabajos.

     

    Programador de tareas

    Otro enfoque que consideramos utiliza TaskScheduler del marco .NET . Hay algunas formas de crear un programador de tareas que limite la concurrencia y garantice FIFO, pero para simplificar, usamos este de MSDN .

    // Initializes the thread queue var taskScheduler = new LimitedConcurrencyLevelTaskScheduler(1); var taskFactory = new TaskFactory(taskScheduler);// Enqueues work taskFactory.StartNew(() ={ if (... /* Check disposed */) return; try { JavaScriptContext.RunScript(... /* JavaScript */); } catch (Exception ex) { ... /* Handle exception */}});

    La ventaja de este enfoque es que no requiere ninguna operación de bloqueo.

    Consideraciones sobre el tiempo de ejecución de ChakraCore

    Recolección de basura

    Uno de los principales obstáculos para usar ChakraCore junto con otro lenguaje administrado como C# es la complejidad de los recolectores de basura que compiten. ChakraCore tiene algunos ganchos para brindarle más control sobre cómo se administra la recolección de basura en el tiempo de ejecución de JavaScript. Para obtener más información, consulte la documentación sobre el uso de recursos en tiempo de ejecución .

    Conclusión: ¿JIT o no JIT?

    Dependiendo de su aplicación, es posible que desee comparar la sobrecarga del compilador JIT con la frecuencia con la que ejecuta ciertas funciones. En caso de que decida que la sobrecarga del compilador JIT no vale la pena, aquí le mostramos cómo puede desactivarlo:

    var runtime = JavaScriptRuntime.Create(JavaScriptRuntimeAttributes.DisableNativeCodeGeneration);

    La opción de ejecutar el compilador justo a tiempo (JIT) en ChakraCore también es opcional: todo el código JavaScript se interpretará completamente incluso sin el compilador JIT.

    Más prácticas con aplicaciones web y móviles

    Consulte estos recursos útiles sobre cómo crear aplicaciones web y móviles:

    • Guía de aplicaciones de la Plataforma universal de Windows (UWP)
    • Lleve su código existente a Windows con Windows Bridges
    • Cursos y tutoriales de desarrollo de aplicaciones
    • C# / XAML Cursos y tutoriales
    • Servicios de aplicaciones de Azure

    Para obtener más actualizaciones sobre el tema y nuevas funciones, consulte la documentación original .

    Otras lecturas

    • Aprender JavaScript: conceptos básicos y pautas
    • Dentro del nuevo motor de renderizado de Microsoft para el "Proyecto Spartan"
    • Representación del lado del servidor con React, Node y Express
    • Una guía para principiantes sobre clientes API JSON basados ​​en jQuery

    (sra, en, il, mrn)Explora más en

    • Móvil
    • javascript
    • Reaccionar





    Tal vez te puede interesar:

    1. Creación de su propia biblioteca de validación de React: la experiencia del desarrollador (Parte 3)
    2. Introducción a Quasar Framework: creación de aplicaciones multiplataforma
    3. Creación de un componente web retro que se puede arrastrar con iluminación
    4. Creación y acoplamiento de una aplicación Node.js con arquitectura sin estado con la ayuda de Kinsta

    Creación de aplicaciones híbridas con ChakraCore

    Creación de aplicaciones híbridas con ChakraCore

    Hola, ChakraCore.Creación de aplicaciones con ChakraCoreTipos de valores de JavaScript en ChakraCoreRequisitos de enhebrado de ChakraCoreConsideraciones sobre

    programar

    es

    https://pseint.es/static/images/programar-creacion-de-aplicaciones-hibridas-con-chakracore-903-0.jpg

    2024-05-20

     

    Creación de aplicaciones híbridas con ChakraCore
    Creación de aplicaciones híbridas con ChakraCore

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Update cookies preferences