πŸ“œ ⬆️ ⬇️

What you need to know about JavaScript Engine Switcher 3.0

Logo of the third version of JavaScript Engine Switcher


JavaScript Engine Switcher was originally created as a supporting library and its development was largely determined by the needs of the libraries that used it. In fact, each major version of it solved one or several major tasks necessary for the further development of other libraries:


  1. In the first version, the task was to add as many adapter modules as possible for the popular JS engines that support the .NET platform. And this gave Bundle Transformer users some flexibility: on development computers they could use the MSIE module, which supports debugging of JS code using Visual Studio, and on servers that did not have a modern version of Internet Explorer or it was not installed at all, they could V8 module. Some even managed to run the Bundle Transformer in Mono on Linux and Mac using the Jurassic and Jint modules.
  2. The main objective of the second version was the implementation of .NET Core support, which was required for the new version of the ReactJS.NET library. Another important task was to create a cross-platform module capable of quickly processing large volumes of JS code (the Jurassic and Jint modules were not suitable for this), and such a module, after a number of improvements, was the ChakraCore module.
  3. In the third version, the main focus was on improving integration with the ReactJS.NET library and increasing productivity.

In this article, we will look at some of the innovations of the third version, which for many turned out to be not obvious even after reading the release text and the β€œHow to upgrade applications to version 3.X” section of the documentation: changes in the JsEngineSwitcher class, reorganization of exceptions, more informative error messages, interruption and pre-compiling scripts, the ability to change the maximum stack size in the modules ChakraCore and MSIE, as well as a new module based on NiL.JS.


JsEngineSwitcher class changes


In the new version, the JsEngineSwitcher class implements the IJsEngineSwitcher interface and is no longer a singleton (its instance can be created using the new operator). To get a global instance, use the Current property instead of the Instance property. The Current property, unlike the obsolete Instance property, has the return type IJsEngineSwitcher . Also, the Current property has a setter, with which you can replace the standard implementation with your own:


 JsEngineSwitcher.Current = new MyJsEngineSwitcher(); 

In ASP.NET Core web applications where the JavaScriptEngineSwitcher.Extensions.MsDependencyInjection package is installed, the implementation is replaced using the AddJsEngineSwitcher extension AddJsEngineSwitcher :


 … using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; … public class Startup { … public void ConfigureServices(IServiceCollection services) { … services.AddJsEngineSwitcher(new MyJsEngineSwitcher(), options => … ) … ; … } … } … 

These changes almost always β€œbreak” applications or libraries that use the previous version of JavaScript Engine Switcher. Therefore, you need to make the following changes to your code:


  1. Change the type of variables, parameters, or properties from JsEngineSwitcher to IJsEngineSwitcher .
  2. Instead of using the Instance property, use the Current property everywhere.

It is also worth noting that for most developers, these changes will not be particularly useful, because their main goal was to simplify unit tests (for example, blocking was used in the unit tests of the ReactJS.NET library, and now you can do without them ).


Reorganization of exceptions


Prior to the third version, most of the errors of JS engines turned into exceptions of the JsRuntimeException type, and only errors that occurred during the engine initialization process turned into a JsEngineLoadException . There was also a base class JsException , from which the two types of exceptions listed above inherited, which allowed to intercept absolutely all the errors that occurred during the work of JS engines. Despite obvious shortcomings, such an organization of exceptions fit well into the concept of a unified interface for accessing the basic features of JS engines.


But when realizing the possibilities of interruption and preliminary compilation of scripts (I will talk about them in the next sections), a need arose for a new approach to the organization of exceptions. The first thing to do was to add a new exception type - JsInterruptedException , which was necessary to notify the user about the interruption of script execution. Then it was necessary to explicitly divide all errors that occurred while processing scripts into two groups: compilation errors (syntax analysis) and runtime errors. Also required to separate all sorts of specific errors Chakra and V8, which were not associated with the processing of scripts. It was also necessary to take into account the presence in the Jint engine of an exception that occurs when the script execution timeout (timeout) expires. As a result, a new approach to the organization of exceptions was formed, which can be represented as the following hierarchical structure:



* - this exception occurs not at the level of the JS engine, but at the level of JavaScript Engine Switcher.


I think that the hierarchy of exceptions presented above does not need comments, because the names of the exceptions speak for themselves. With this approach, we get not only more information about the causes of the error, but we can more flexibly handle certain types of exceptions.


Unified error message format


Another problem with previous versions of JavaScript Engine Switcher was the difficulty in locating errors that occurred during script processing. It was difficult to understand exactly where the error occurred from the Message exception property, so we had to analyze other exception properties, which was not always convenient. In addition, a set of existing properties was also insufficient.


Therefore, 2 new properties have been added to the JsScriptException class:


  1. Type - type of JavaScript error (for example, SyntaxError or TypeError );
  2. DocumentName is the name of the document (usually derived from the values ​​of the following parameters: documentName of the Execute and Evaluate methods, path the ExecuteFile method, resourceName the ExecuteResource method, etc.);

One new property has also been added to the JsRuntimeException class - CallStack , which contains a string representation of the call stack.


Previously, the Message property simply copied a value from a similar property of the original .NET exception or a string representation of a JavaScript error. Often, error messages in different JS engines differed not only in the format, but also in the amount of useful information presented in them. For example, due to the lack of information about the row and column numbers in some error messages, the developers of the ReactJS.NET library were forced to override the exceptions obtained from the JavaScript Engine Switcher.


Therefore, I decided to generate my own error messages at the level of adapter modules that would have a single (unified) format. This format uses all available error information: type, description, document name, line number, column number, code fragment, and call stack. As a basis for the new format, I took the error format from the Microsoft ClearScript library.


Below are the messages about the same compilation error that were generated by different adapter modules:


 ChakraCore ========== SyntaxError: Unexpected identifier after numeric literal at declinationOfSeconds.js:12:23 -> caseIndex = number % 1O < 5 ? number % 10 : 5; Jint ==== SyntaxError: Unexpected token ILLEGAL at declinationOfSeconds.js:12:25 Jurassic ======== SyntaxError: Expected operator but found 'O' at declinationOfSeconds.js:12 MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Classic ===================== SyntaxError: Expected ';' at declinationOfSeconds.js:12:25 -> caseIndex = number % 1O < 5 ? number % 10 : 5; MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra ActiveScript ================================= SyntaxError: Expected ';' at declinationOfSeconds.js:12:25 -> caseIndex = number % 1O < 5 ? number % 10 : 5; MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra IE JsRT ============================ SyntaxError: Expected ';' at 12:25 -> caseIndex = number % 1O < 5 ? number % 10 : 5; MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra Edge JsRT ============================== SyntaxError: Unexpected identifier after numeric literal at declinationOfSeconds.js:12:23 -> caseIndex = number % 1O < 5 ? number % 10 : 5; NiL === SyntaxError: Unexpected token 'O' at 12:25 V8 == SyntaxError: Invalid or unexpected token at declinationOfSeconds.js:12:24 -> caseIndex = number % 1O < 5 ? number % 10 : 5; Vroom ===== SyntaxError: Unexpected token ILLEGAL at declinationOfSeconds.js:12:24 

A similar example for a runtime error:


 ChakraCore ========== TypeError: Unable to get property 'Π§' of undefined or null reference at transliterate (russian-translit.js:929:4) -> newCharValue = typeof charMapping[charValue] !== 'undefined' ? at Global code (Script Document:1:1) Jint ==== TypeError: charMapping is undefined at russian-translit.js:929:26 Jurassic ======== TypeError: undefined cannot be converted to an object at transliterate (russian-translit.js:929) at Global code (Script Document:1) MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Classic ===================== TypeError: 'undefined' is null or not an object at russian-translit.js:929:4 MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra ActiveScript ================================= TypeError: Unable to get property 'Π§' of undefined or null reference at russian-translit.js:929:4 MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra IE JsRT ============================ TypeError: Unable to get property 'Π§' of undefined or null reference at transliterate (russian-translit.js:929:4) at Global code (Script Document:1:1) MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra Edge JsRT ============================== TypeError: Unable to get property 'Π§' of undefined or null reference at transliterate (russian-translit.js:929:4) at Global code (Script Document:1:1) NiL === TypeError: Can't get property "Π§" of "undefined" V8 == TypeError: Cannot read property 'Π§' of undefined at transliterate (russian-translit.js:929:37) -> newCharValue = typeof charMapping[charValue] !== 'undefined' ? at Script Document:1:1 Vroom ===== TypeError: Cannot read property 'Π§' of undefined at russian-translit.js:929:37 

From the examples it can be seen that some JS engines give us completely different error descriptions and column numbers, and not always we can get a complete set of error data, but even despite these shortcomings, the unified format gives us more information about the location of the error than original error messages.


Tips for deploying native builds


The main cause of errors when working with the second version of JavaScript Engine Switcher was that many developers forgot to install NuGet packages containing native assemblies for ChakraCore and V8 modules. At one time, this post was even devoted to a post in the ReactJS.NET bugtracker (a Russian translation is also available). Now, this is mostly a bug for newbies who, for some reason, have not read the documentation.


The authors of ReactJS.NET tried to minimize the number of such errors with the help of hints inside error messages, but the not very successful implementation of this approach led to even more confusion . The idea of ​​the prompts was good, but it required a fundamentally different implementation, namely, the implementation of JS engines at the level of adapter modules. In the new version of JavaScript Engine Switcher, such hints are added to the error message when wrapping DllNotFoundException and TypeLoadException exceptions into a JsEngineLoadException exception (see the implementation examples for ChakraCore , V8, and Vroom modules). Moreover, these tips are intelligent, because their generation takes into account a number of factors: the type of operating system, processor architecture and runtime (.NET Framework, .NET Core or Mono).


For example, if you use the ChakraCore module without a native build in a 64-bit process on the Windows operating system, the error message will look like this:


Failed to create an instance of the ChakraCoreJsEngine. Most likely it happened, because the dependencies were not found. Try to install the JavaScriptEngineSwitcher.ChakraCore.Native.win-x64 package via NuGet. In addition, you still need to install the Microsoft Visual C ++ Redistributable for Visual Studio 2017 ( https://www.visualstudio.com/downloads/#microsoft-visual-c-redistributable-for-visual-studio-2017 ).

The error message prompts you to install the NuGet package JavaScriptEngineSwitcher.ChakraCore.Native.win-x64, and also mentions that ChakraCore for Windows requires a Microsoft Visual C ++ redistributable component for Visual Studio 2017 to work. If this error will occur in a 32-bit process, the user will be prompted to install the JavaScriptEngineSwitcher.ChakraCore.Native.win-x86 package.


A similar error message on Linux in the .NET Core environment will look like this:


Failed to create an instance of the ChakraCoreJsEngine. Most likely it happened, because of its dependencies was not found. Try to install the JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 package via NuGet.

In this case, you will be prompted to install the JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 package.


When launched in Mono, another hint will be displayed:


... JavaScriptEngineSwitcher.ChakraCore.Native.linux- * package

Since the JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 package is only compatible with .NET Core, the prompt will link to instructions for manually deploying the native build in Linux.


You can still give many examples, but this makes no sense.


Interrupting Script Execution


When we give users the ability to execute arbitrary JS code on the server, we face one very serious problem - we don’t know how long it will take to execute this code. This may be a large amount of non-optimal code or a code that starts an infinite loop. In any case, this will be code that we cannot control, which will consume our server resources indefinitely. In order to somehow control this process, we need the ability to interrupt the execution of scripts. When using engines written in pure .NET (for example, Jint, Jurassic or NiL.JS), we can always start executing JS-code as a task with the possibility of cancellation, but this approach will not work for other engines. Fortunately for us, the engines written in C ++ have built-in mechanisms for interrupting scripts.


To provide access to these mechanisms, the SupportsScriptInterruption property and the Interrupt method IJsEngine been added to the IJsEngine interface. Since not all engines support this feature, you should always check the value of the SupportsScriptInterruption property before calling the Interrupt method (if you had to manually start the garbage collector in previous versions of JavaScript Engine Switcher, you will immediately understand what I am talking about):


 if (engine.SupportsScriptInterruption) { engine.Interrupt(); } 

Moreover, it is necessary to call this method in a separate thread other than the one in which the scripts are executed. After calling the Interrupt method, all previously run Evaluate , Execute* and CallFunction will be completed by throwing a JsInterruptedException exception.


Since this API is low-level, for the tasks described in the beginning of the section it is recommended to use extension methods like this:


 using System; #if !NET40 using System.Runtime.ExceptionServices; #endif using System.Threading; using System.Threading.Tasks; using JavaScriptEngineSwitcher.Core; #if NET40 using JavaScriptEngineSwitcher.Core.Extensions; #endif using JavaScriptEngineSwitcher.Core.Resources; … /// <summary> /// Extension methods for <see cref="IJsEngine"/> /// </summary> public static class JsEngineExtensions { /// <summary> /// Evaluates an expression within a specified time interval /// </summary> /// <typeparam name="T">Type of result</typeparam> /// <param name="engine">JS engine</param> /// <param name="expression">JS expression</param> /// <param name="timeoutInterval">Interval to wait before the /// script execution times out</param> /// <param name="documentName">Document name</param> /// <returns>Result of the expression</returns> /// <exception cref="ObjectDisposedException"/> /// <exception cref="ArgumentNullException"/> /// <exception cref="ArgumentException"/> /// <exception cref="JsCompilationException"/> /// <exception cref="JsTimeoutException"/> /// <exception cref="JsRuntimeException"/> /// <exception cref="JsException"/> public static T Evaluate<T>(this IJsEngine engine, string expression, TimeSpan timeoutInterval, string documentName) { if (engine == null) { throw new ArgumentNullException(nameof(engine)); } if (engine.SupportsScriptInterruption) { using (var timer = new Timer(state => engine.Interrupt(), null, timeoutInterval, #if NET40 new TimeSpan(0, 0, 0, 0, -1))) #else Timeout.InfiniteTimeSpan)) #endif { try { return engine.Evaluate<T>(expression, documentName); } catch (JsInterruptedException e) { throw new JsTimeoutException( Strings.Runtime_ScriptTimeoutExceeded, e.EngineName, e.EngineVersion, e ); } } } else { #if NET40 Task<T> task = Task.Factory.StartNew(() => #else Task<T> task = Task.Run(() => #endif { return engine.Evaluate<T>(expression, documentName); }); bool isCompletedSuccessfully = false; try { isCompletedSuccessfully = task.Wait(timeoutInterval); } catch (AggregateException e) { Exception innerException = e.InnerException; if (innerException != null) { #if NET40 innerException.PreserveStackTrace(); throw innerException; #else ExceptionDispatchInfo.Capture(innerException).Throw(); #endif } else { throw; } } if (isCompletedSuccessfully) { return task.Result; } else { throw new JsTimeoutException( Strings.Runtime_ScriptTimeoutExceeded, engine.Name, engine.Version ); } } } … } … 

This method is an add-on over the Evaluate<T> engine method, which allows setting the timeout for the script execution using the timeoutInterval parameter. The principle of this extension method is very simple. First, we check if our engine supports the built-in interrupt mechanism. If it timeoutInterval , then we create an instance of the Timer class, which, through the timeoutInterval parameter, starts the Interrupt method, then we call the Evaluate<T> engine method, and in case of an error, we catch the JsInterruptedException exception and wrap it in a JsTimeoutException . If the engine does not support interrupts, then create an instance of the Task class that runs the Evaluate<T> engine method, then set the task timeout interval equal to the value from the timeoutInterval parameter, and if the task timed out, then throw an exception of type JsTimeoutException . The following is an example of using this extension method:


 using System; … using JavaScriptEngineSwitcher.Core; using JavaScriptEngineSwitcher.Core.Helpers; … class Program { … static void Main(string[] args) { const string expression = @"function getRandomInt(minValue, maxValue) { minValue = Math.ceil(minValue); maxValue = Math.floor(maxValue); return Math.floor(Math.random() * (maxValue - minValue + 1)) + minValue; } function sleep(millisecondsTimeout) { var totalMilliseconds = new Date().getTime() + millisecondsTimeout; while (new Date().getTime() < totalMilliseconds) { } } var randomNumber = getRandomInt(1, 10); sleep(randomNumber * 1000); randomNumber;"; using (IJsEngine engine = JsEngineSwitcher.Current.CreateDefaultEngine()) { try { int result = engine.Evaluate<int>(expression, TimeSpan.FromSeconds(3), "randomNumber.js"); Console.WriteLine("Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ = {0}", result); } catch (JsTimeoutException) { Console.WriteLine("Π’ΠΎ врСмя вычислСния выраТСния JavaScript " + "Π±Ρ‹Π»ΠΎ ΠΏΡ€Π΅Π²Ρ‹ΡˆΠ΅Π½ΠΎ врСмя оТидания!"); } catch (JsException e) { Console.WriteLine("Π’ΠΎ врСмя Ρ€Π°Π±ΠΎΡ‚Ρ‹ JavaScript-Π΄Π²ΠΈΠΆΠΊΠ° ΠΏΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° " + "ошибка!"); Console.WriteLine(); Console.WriteLine(JsErrorHelpers.GenerateErrorDetails(e)); } } } … } … 

Using the extension method, we calculate the result of a JS expression. The result of the expression is a random integer, and this number is also the number of seconds by which the execution of this expression is delayed. Also, when calling the extension method, we specify a wait interval of 3 seconds. Since the time to calculate the expression varies from 1 to 10 seconds, in one case the result of the expression will be displayed on the console, and in the other the message about waiting time is displayed.


It should be easy for you to remake this extension method to call other methods of the engine (for example, under the Execute* , CallFunction or other overloaded versions of the Evaluate method). While I have not yet decided whether I will add such extension methods to the library itself, because it is not yet clear how much they will be in demand.


Pre-compiling scripts


I was asked to add support for pre-compiling scripts back in 2015 . At that moment, it was not clear how such a function could fit into the concept of a unified interface. But as time went on, support for special features such as garbage collection and script interruption began to appear in JavaScript Engine Switcher. At the last stages of work on the third version, the turn reached the preliminary compilation.


Using precompilation, you can compile the script once, and then use it many times to initialize the JS engines. Due to the fact that the pre-compiled script does not require syntactic analysis, the initialization of the engines will be faster.


At the moment, 5 modules-adapters support preliminary compilation: ChakraCore, Jint, Jurassic, MSIE (only in JsRT-modes) and V8. To provide access to the corresponding engine mechanisms, the SupportsScriptPrecompilation property and 3 new methods were added to the IJsEngine interface: Precompile , PrecompileFile and PrecompileResource . Precompile* methods return an instance of an object that implements the IPrecompiledScript interface. This object is a pre-compiled script that can be used by different instances of engines (for these purposes serves as an overloaded version of the Execute method). Consider a simple example of using this API:


 using System; … using JavaScriptEngineSwitcher.Core; using JavaScriptEngineSwitcher.Core.Helpers; … class Program { … static void Main(string[] args) { const string sourceCode = @"function declinationOfSeconds(number) { var result, titles = ['сСкунда', 'сСкунды', 'сСкунд'], titleIndex, cases = [2, 0, 1, 1, 1, 2], caseIndex ; if (number % 100 > 4 && number % 100 < 20) { titleIndex = 2; } else { caseIndex = number % 10 < 5 ? number % 10 : 5; titleIndex = cases[caseIndex]; } result = number + ' ' + titles[titleIndex]; return result; }"; const string functionName = "declinationOfSeconds"; const int itemCount = 4; int[] inputSeconds = new int[itemCount] { 0, 1, 42, 600 }; string[] outputStrings = new string[itemCount]; IJsEngineSwitcher engineSwitcher = JsEngineSwitcher.Current; IPrecompiledScript precompiledCode = null; using (var engine = engineSwitcher.CreateDefaultEngine()) { if (!engine.SupportsScriptPrecompilation) { Console.WriteLine("{0} вСрсии {1} Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ " + "ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΡƒΡŽ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡ†ΠΈΡŽ скриптов!", engine.Name, engine.Version); return; } try { precompiledCode = engine.Precompile(sourceCode, "declinationOfSeconds.js"); engine.Execute(precompiledCode); outputStrings[0] = engine.CallFunction<string>(functionName, inputSeconds[0]); } catch (JsCompilationException e) { Console.WriteLine("Π’ΠΎ врСмя ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ компиляции скрипта " + "ΠΏΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° ошибка!"); Console.WriteLine(); Console.WriteLine(JsErrorHelpers.GenerateErrorDetails(e)); return; } catch (JsException e) { Console.WriteLine("Π’ΠΎ врСмя Ρ€Π°Π±ΠΎΡ‚Ρ‹ JavaScript-Π΄Π²ΠΈΠΆΠΊΠ° ΠΏΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° " + "ошибка!"); Console.WriteLine(); Console.WriteLine(JsErrorHelpers.GenerateErrorDetails(e)); return; } } for (int itemIndex = 1; itemIndex < itemCount; itemIndex++) { using (var engine = engineSwitcher.CreateDefaultEngine()) { try { engine.Execute(precompiledCode); outputStrings[itemIndex] = engine.CallFunction<string>( functionName, inputSeconds[itemIndex]); } catch (JsException e) { Console.WriteLine("Π’ΠΎ врСмя Ρ€Π°Π±ΠΎΡ‚Ρ‹ JavaScript-Π΄Π²ΠΈΠΆΠΊΠ° " + "ΠΏΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° ошибка!"); Console.WriteLine(); Console.WriteLine(JsErrorHelpers.GenerateErrorDetails(e)); return; } } } for (int itemIndex = 0; itemIndex < itemCount; itemIndex++) { Console.WriteLine(outputStrings[itemIndex]); } } … } … 

Π‘Π½Π°Ρ‡Π°Π»Π° ΠΌΡ‹ создаСм Π΄Π²ΠΈΠΆΠΎΠΊ, с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ скомпилируСм ΠΊΠΎΠ΄ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ для склонСния Ρ‡ΠΈΡΠ»ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Ρ…. ПослС Ρ‡Π΅Π³ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ свойство SupportsScriptPrecompilation для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ Π»ΠΈ Π΄Π²ΠΈΠΆΠΎΠΊ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΡƒΡŽ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡ†ΠΈΡŽ, Ссли Π½Π΅Ρ‚, Ρ‚ΠΎ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠΈΡ€ΡƒΠ΅ΠΌ ΠΎΠ± этом ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ. Π—Π°Ρ‚Π΅ΠΌ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Precompile ΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡ€ΡƒΠ΅ΠΌ скрипт, Ссли ΠΊΠΎΠ΄ скрипта содСрТит синтаксичСскиС ошибки, Ρ‚ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π±Ρ€ΠΎΡˆΠ΅Π½ΠΎ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ Ρ‚ΠΈΠΏΠ° JsCompilationException . C ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Execute Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌ скомпилированный скрипт Π² ΠΏΠ°ΠΌΡΡ‚ΡŒ Π΄Π²ΠΈΠΆΠΊΠ°, Ρ‚.Π΅. ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΠΌ Π΅Π³ΠΎ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ. Π—Π°Ρ‚Π΅ΠΌ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° CallFunction Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ declinationOfSeconds ΠΈ сохраняСм ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹ΠΉ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π² массив. ПослС Ρ‡Π΅Π³ΠΎ происходит ΡƒΠ½ΠΈΡ‡Ρ‚ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π΄Π²ΠΈΠΆΠΊΠ°. НСсмотря Π½Π° Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Π΄Π²ΠΈΠΆΠΎΠΊ Π±Ρ‹Π» ΡƒΠ½ΠΈΡ‡Ρ‚ΠΎΠΆΠ΅Π½, скомпилированный ΠΈΠΌ скрипт ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅Ρ‚ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΠΎΠ²Π°Ρ‚ΡŒ ΠΈ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ использован Π΄Ρ€ΡƒΠ³ΠΈΠΌΠΈ Π΄Π²ΠΈΠΆΠΊΠ°ΠΌΠΈ. Π”Π°Π»Π΅Π΅ ΠΌΡ‹ создаСм Π΅Ρ‰Π΅ 3 Π΄Π²ΠΈΠΆΠΊΠ° ΠΈ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΈΠ· Π½ΠΈΡ… ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΡƒΠ΅ΠΌ скомпилированным скриптом. ПослС ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ, ΠΊΠ°ΠΊ ΠΈ Π² случаС с самым ΠΏΠ΅Ρ€Π²Ρ‹ΠΌ Π΄Π²ΠΈΠΆΠΊΠΎΠΌ, Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΈ сохраняСм Π΅Π΅ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π² массив. Π’ ΠΊΠΎΠ½Ρ†Π΅ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° Π²Ρ‹Π²ΠΎΠ΄ΠΈΠΌ содСрТимоС этого массива Π½Π° экран, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ΄ΠΎΡΡ‚ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒΡΡ Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ инициализация Π΄Π²ΠΈΠΆΠΊΠΎΠ² ΠΏΡ€ΠΎΡˆΠ»Π° Π±Π΅Π· ошибок.


ΠŸΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰ΠΈΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Ρ…ΠΎΡ‚ΡŒ ΠΈ наглядно ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚, ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΡƒΡŽ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡ†ΠΈΡŽ, Π½ΠΎ являСтся Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ искусствСнным. Π’ Ρ€Π΅Π°Π»ΡŒΠ½Ρ‹Ρ… ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°Ρ…, скорСС всСго, Π²Ρ‹ Π±ΡƒΠ΄Π΅Ρ‚Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡƒΠ»ΠΈΠ½Π³ Π΄Π²ΠΈΠΆΠΊΠΎΠ² ΠΈ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ скомпилированныС скрипты Π² кэшС. ИмСнно Ρ‚Π°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ ReactJS.NET. ΠŸΡƒΠ»ΠΈΠ½Π³ Π΄Π²ΠΈΠΆΠΊΠΎΠ² Ρ‚Π°ΠΌ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ JSPool , Π° Π²Ρ‹Π±ΠΎΡ€ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ кэша зависит ΠΎΡ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΠΎΠ³ΠΎ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠ° ( System.Runtime.Caching.MemoryCache для .NET Framework 4.X, System.Web.Caching.Cache для ASP.NET 4.X ΠΈ Microsoft.Extensions.Caching.Memory.IMemoryCache для ASP.NET Core). НС Π±ΡƒΠ΄Ρƒ Π²Π΄Π°Π²Π°Ρ‚ΡŒΡΡ Π² Π΄Π΅Ρ‚Π°Π»ΠΈ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ это Π·Π°ΠΉΠΌΠ΅Ρ‚ слишком ΠΌΠ½ΠΎΠ³ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ (ΠΏΡ€ΠΈ ΠΆΠ΅Π»Π°Π½ΠΈΠΈ Π²Ρ‹ всСгда ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡ‚ΡŒΡΡ с исходным ΠΊΠΎΠ΄ΠΎΠΌ ). Π›ΡƒΡ‡ΡˆΠ΅ вмСсто этого рассмотрим, ΠΊΠ°ΠΊ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΡƒΡŽ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡ†ΠΈΡŽ скриптов Π² ReactJS.NET.


По ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½Π°Ρ компиляция Π² ReactJS.NET ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½Π°. ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ эта Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΏΠΎΠΊΠ° считаСтся ΡΠΊΡΠΏΠ΅Ρ€ΠΈΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΈ Π΅Ρ‰Π΅ Π½Π΅ ΠΏΡ€ΠΈΠΌΠ΅Π½ΡΠ»Π°ΡΡŒ Π½Π° Ρ€Π΅Π°Π»ΡŒΠ½Ρ‹Ρ… высоконагруТСнных сайтах. Π§Ρ‚ΠΎΠ±Ρ‹ Π΅Π΅ Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Π½ΡƒΠΆΠ½ΠΎ ΠΏΡ€ΠΈΡΠ²ΠΎΠΈΡ‚ΡŒ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠΌΡƒ свойству AllowJavaScriptPrecompilation Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Ρ€Π°Π²Π½ΠΎΠ΅ true .


Π’ ASP.NET 4.X для этого Π²Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ ΠΎΡ‚Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ» App_Start/ReactConfig.cs :


 … public static class ReactConfig { public static void Configure() { ReactSiteConfiguration.Configuration … .SetAllowJavaScriptPrecompilation(true) … ; … } } … 

Π’ ASP.NET Core эта настройка производится Π² Ρ„Π°ΠΉΠ»Π΅ Startup.cs :


 … public class Startup { … public void Configure(IApplicationBuilder app, IHostingEnvironment env) { … app.UseReact(config => { config … .SetAllowJavaScriptPrecompilation(true) … ; }); app.UseStaticFiles(); … } } … 

Π’ ΠΊΠΎΠ½Ρ†Π΅ Ρ€Π°Π·Π΄Π΅Π»Π° я Ρ…ΠΎΡ‡Ρƒ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ Π²Π°ΠΌ, ΠΊΠ°ΠΊ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½Π°Ρ компиляция влияСт Π½Π° ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ. Π’ качСствС ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° рассмотрим Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ Π±Π΅Π½Ρ‡ΠΌΠ°Ρ€ΠΊΠ° JsExecutionBenchmark , Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ срСдствами Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ BenchmarkDotNet . Π’ качСствС исполняСмого скрипта здСсь ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ нСбольшая JS-Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° для транслитСрации русского тСкста Π»Π°Ρ‚ΠΈΠ½ΠΈΡ†Π΅ΠΉ. ВСсит ΠΎΠ½Π° всСго 14,9 ΠšΠ±Π°ΠΉΡ‚, Π½ΠΎ этого Π½Π°ΠΌ Π±ΡƒΠ΄Π΅Ρ‚ достаточно, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ эффСкт ΠΎΡ‚ примСнСния ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ компиляции. Π’ΠΎ врСмя тСстирования использовалась послСдняя Π½Π° Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ вСрсия JavaScript Engine Switcher (вСрсия 3.0.4).


НачнСм с Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ², ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹Ρ… ΠΏΡ€ΠΈ запускС Π² срСдС .NET Framework 4.7.2:


НазваниС ΠΌΠΎΠ΄ΡƒΠ»ΡΠŸΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚. компиляцияБрСдняя ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚. выполнСнияGen 0 Π½Π° тыс. ΠΎΠΏΠ΅Ρ€.Gen 1 Π½Π° тыс. ΠΎΠΏΠ΅Ρ€.Gen 2 Π½Π° тыс. ΠΎΠΏΠ΅Ρ€.Π’Ρ‹Π΄Π΅Π»Π΅Π½ΠΈΠ΅ памяти Π½Π° ΠΎΠ΄Π½Ρƒ ΠΎΠΏΠ΅Ρ€.
ChakraCoreNot41,72 мс---74,46 Кб
Yes35,07 мс---91,79 Кб
JintNot27,19 мс2 812,501 343,75-16 374,58 Кб
Yes15,54 мс1 296,88640,6331,257 521,49 Кб
JurassicNot455,70 мс2 000,001 000,00-15 575,28 Кб
Yes78,70 мс1 000,00--7 892,94 Кб
MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra IE JsRTNot30,97 мс---77,75 Кб
Yes24,40 мс---90,58 Кб
MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra Edge JsRTNot33,14 мс---78,40 Кб
Yes32,86 мс---95,48 Кб
V8Not41,10 мс---79,33 Кб
Yes39,25 мс---96,17 Кб

Наибольший Π²Ρ‹ΠΈΠ³Ρ€Ρ‹Ρˆ Π² ΠΏΡ€ΠΎΡ†Π΅Π½Ρ‚Π½ΠΎΠΌ Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ для Π΄Π²ΠΈΠΆΠΊΠΎΠ², написанных Π½Π° чистом .NET β€” ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ выполнСния скрипта Π½Π° Jurassic вырастаСт 5,79 Ρ€Π°Π·, Π° Π½Π° Jint Π² 1,75 Ρ€Π°Π·a. ΠŸΡ€ΠΈ Ρ€Π°Π±ΠΎΡ‚Π΅ этих Π΄Π²ΠΈΠΆΠΊΠΎΠ² Π² 2 Ρ€Π°Π·Π° сокращаСтся объСм выдСляСмой памяти, ΠΈ соотвСтствСнно Π±ΠΎΠ»Π΅Π΅ Ρ‡Π΅ΠΌ Π² 2 Ρ€Π°Π·Π° сокращаСтся количСство ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ ΠΏΠΎ сборкС мусора. Π—Π° счСт сниТСния количСства ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ ΠΏΠΎ сборкС мусора ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ частичный прирост Π² скорости выполнСния скрипта. Главная ΠΏΡ€ΠΈΡ‡ΠΈΠ½Π° прироста скорости Π² Jurassic, Ρ‚Π°ΠΊΠΆΠ΅ являСтся ΠΈ ΠΏΡ€ΠΈΡ‡ΠΈΠ½ΠΎΠΉ Π΅Π³ΠΎ Π½Π°ΠΈΡ…ΡƒΠ΄ΡˆΠ΅Π³ΠΎ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π° Π² этом тСстС: Jurassic всСгда ΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡ€ΡƒΠ΅Ρ‚ JS-ΠΊΠΎΠ΄ Π² IL-ΠΊΠΎΠ΄ срСдствами Reflection.Emit, Π° Π·Π°Ρ‚Π΅ΠΌ исполняСт Π΅Π³ΠΎ. ИмСнно Π½Π° этапС компиляции происходит основная потСря ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ, Π° ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΏΡ€ΠΈ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ компиляции это происходит всСго ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π·, ΠΌΡ‹ ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ Π²Ρ‹ΠΈΠ³Ρ€Ρ‹Ρˆ Π² ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ. Jint ΠΆΠ΅ Π½Π°ΠΎΠ±ΠΎΡ€ΠΎΡ‚ являСтся ΠΈΠ½Ρ‚Π΅Ρ€ΠΏΡ€Π΅Ρ‚Π°Ρ‚ΠΎΡ€ΠΎΠΌ ΠΈ Π² Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ компиляции Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ .NET-ΠΎΠ±ΡŠΠ΅ΠΊΡ‚, ΠΏΡ€Π΅Π΄ΡΡ‚Π°Π²Π»ΡΡŽΡ‰ΠΈΠΉ абстрактноС синтаксичСскоС Π΄Π΅Ρ€Π΅Π²ΠΎ. Π’ случаС с Jint ΠΌΡ‹ экономим рСсурс Π·Π° счСт Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΠΌ всСго ΠΎΠ΄Π½Ρƒ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΡŽ синтаксичСского Ρ€Π°Π·Π±ΠΎΡ€Π° ΠΈ Ρ…Ρ€Π°Π½ΠΈΠΌ Π² памяти ΠΎΠ΄Π½ΠΎ АБД. По Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°ΠΌ Π΄Π°Π½Π½ΠΎΠ³ΠΎ тСста ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ΄ΡƒΠΌΠ°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎ Jint являСтся самым быстрым Π΄Π²ΠΈΠΆΠΊΠΎΠΌ, Π½ΠΎ это Π½Π΅ Ρ‚Π°ΠΊ, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ ΠΏΠΎ ΠΌΠ΅Ρ€Π΅ роста объСма выполняСмого ΠΊΠΎΠ΄Π° Π΅Π³ΠΎ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ°Π΄Π°Ρ‚ΡŒ. Π’ΠΎΠΎΠ±Ρ‰Π΅ Jint ΠΈ Jurassic ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°ΡŽΡ‚ Π½Π°ΠΈΡ…ΡƒΠ΄ΡˆΠΈΠ΅ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ ΠΏΡ€ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ Π±ΠΎΠ»ΡŒΡˆΠΈΡ… объСмов ΠΊΠΎΠ΄Π°.


Π›ΡƒΡ‡ΡˆΠΈΠΉ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ срСди ΠΎΠ±Π΅Ρ€Ρ‚ΠΎΠΊ Π½Π°Π΄ Π΄Π²ΠΈΠΆΠΊΠ°ΠΌΠΈ, написанными Π½Π° C++, ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra IE JsRT β€” ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ выполнСния увСличиваСтся Π½Π° 26,93%. ПослС Π½Π΅Π³ΠΎ ΠΈΠ΄Π΅Ρ‚ ChakraCore (18,96%), ΠΏΠΎΡ‚ΠΎΠΌ V8 (4,71%) ΠΈ MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra Edge JsRT ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ Ρ…ΡƒΠ΄ΡˆΠΈΠΉ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ (0,85%). Для мСня Π΄ΠΎ сих ΠΏΠΎΡ€ остаСтся Π·Π°Π³Π°Π΄ΠΊΠΎΠΉ ΠΏΠΎΡ‡Π΅ΠΌΡƒ Π΄Π²ΠΈΠΆΠΎΠΊ ΠΈΠ· Internet Explorer оказался быстрСС Π΄Π²ΠΈΠΆΠΊΠ° Edge. Π’ΠΎΠΎΠ±Ρ‰Π΅ ΡΡ‚ΠΎΠ»ΡŒ скромныС Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ для Π΄Π°Π½Π½ΠΎΠ³ΠΎ Ρ‚ΠΈΠΏΠ° Π΄Π²ΠΈΠΆΠΊΠΎΠ² ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ±ΡŠΡΡΠ½ΠΈΡ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ. ΠœΡ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ скомпилированный скрипт ΠΎΡ‚ Π΄Π²ΠΈΠΆΠΊΠ° Π² сСриализованном Π²ΠΈΠ΄Π΅ (Π² Π²ΠΈΠ΄Π΅ массива Π±Π°ΠΉΡ‚) ΠΈ сохраняСм Π΅Π³ΠΎ Π² управляСмой памяти. Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π·Π°ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ Π² послСднСм столбцС Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ ΠΈΠ·-Π·Π° появлСния этого массива увСличился объСм выдСляСмой памяти Π½Π° 12-17 ΠšΠ±Π°ΠΉΡ‚ (Π² этом тСстС ΠΌΡ‹ измСряСм Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΡƒΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌΡƒΡŽ ΠΏΠ°ΠΌΡΡ‚ΡŒ). ΠŸΡ€ΠΈ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Π΄Π²ΠΈΠΆΠΊΠ° скомпилированным скриптом производится дСсСриализация этого массива. Π’Π°ΠΊΠΆΠ΅ Π΄ΠΎΠ±Π°Π²ΡŒΡ‚Π΅ ΠΊ этому Π·Π°Ρ‚Ρ€Π°Ρ‚Ρ‹ Π½Π° ΠΌΠ°Ρ€ΡˆΠ°Π»ΠΈΠ½Π³. Π’Π΅ΠΌ Π½Π΅ ΠΌΠ΅Π½Π΅Π΅ Π²Ρ‹ΠΈΠ³Ρ€Ρ‹Ρˆ Π² ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ всС Ρ€Π°Π²Π½ΠΎ ΠΎΡ‰ΡƒΡ‚ΠΈΠΌ.


Запуск тСстов Π² срСдС .NET Core 2.0 Π΄Π°Π΅Ρ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ (ΠΌΠΎΠ΄ΡƒΠ»ΡŒ V8 отсутствуСт, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° Microsoft ClearScript, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ ΠΎΠ½ основан, Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ .NET Core):


НазваниС ΠΌΠΎΠ΄ΡƒΠ»ΡΠŸΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚. компиляцияБрСдняя ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚. выполнСнияGen 0 Π½Π° тыс. ΠΎΠΏΠ΅Ρ€.Gen 1 Π½Π° тыс. ΠΎΠΏΠ΅Ρ€.Gen 2 Π½Π° тыс. ΠΎΠΏΠ΅Ρ€.Π’Ρ‹Π΄Π΅Π»Π΅Π½ΠΈΠ΅ памяти Π½Π° ΠΎΠ΄Π½Ρƒ ΠΎΠΏΠ΅Ρ€.
ChakraCoreNot43,65 мс---18,07 Кб
Yes36,37 мс---16,59 Кб
JintNot24,87 мс2 750,001 375,00-16 300,25 Кб
Yes15,25 мс1 281,25593,7562,507 447,44 Кб
JurassicNot469,97 мс2 000,001 000,00-15 511,70 Кб
Yes80,72 мс1 000,00--7 845,98 Кб
MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra IE JsRTNot31,50 мс---20,28 Кб
Yes24,52 мс---18,78 Кб
MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra Edge JsRTNot35,54 мс---20,45 Кб
Yes31,44 мс---18,99 Кб

Π’ Ρ†Π΅Π»ΠΎΠΌ ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΈ ΠΏΠΎΡ…ΠΎΠΆΠΈΠ΅ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹. ЕдинствСнноС, Ρ‡Ρ‚ΠΎ бросаСтся Π² Π³Π»Π°Π·Π° β€” Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ MSIE Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Chakra Edge JsRT ΡƒΠ»ΡƒΡ‡ΡˆΠΈΠ»ΡΡ (7,69%). Π’Π°ΠΊΠΆΠ΅ Π² Π΄Π°Π½Π½ΠΎΠΌ случаС ΠΏΡ€ΠΈ использовании ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ компиляции Π΄Π²ΠΈΠΆΠΊΠ°ΠΌΠΈ сСмСйства Chakra сниТаСтся ΠΏΠΎΡ‚Ρ€Π΅Π±Π»Π΅Π½ΠΈΠ΅ управляСмой памяти.


Π’ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ измСнСния максимального Ρ€Π°Π·ΠΌΠ΅Ρ€Π° стСка Π² модулях ChakraCore ΠΈ MSIE


Π Π°Π·ΠΌΠ΅Ρ€ стСка Π² Π΄Π²ΠΈΠΆΠΊΠ°Ρ…, Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Π°Π½Π½Ρ‹Ρ… Microsoft, ограничиваСтся Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ стСка ΠΏΠΎΡ‚ΠΎΠΊΠ°, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ выполняСтся Π΄Π²ΠΈΠΆΠΎΠΊ. ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Π² соврСмСнных вСрсиях IIS ΠΎΠ½ достаточно ΠΌΠ°Π» (256 ΠšΠ±Π°ΠΉΡ‚ для 32-разрядной вСрсии ΠΈ 512 ΠšΠ±Π°ΠΉΡ‚ для 64-разрядной), Ρ‚ΠΎ ΠΏΡ€ΠΈ запускС Π² ASP.NET Π±ΠΎΠ»ΡŒΡˆΠΈΡ… JS-Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, компилятора TypeScript) происходит ΠΏΠ΅Ρ€Π΅ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ стСка. Π­Ρ‚Π° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° ΡƒΠΆΠ΅ Π΄Π°Π²Π½ΠΎ Ρ€Π΅ΡˆΠ°Π΅Ρ‚ΡΡ Π² JavaScript Engine Switcher ΠΏΡƒΡ‚Π΅ΠΌ создания ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎΠ³ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠ° для выполнСния этих Π΄Π²ΠΈΠΆΠΊΠΎΠ². РаньшС ΠΏΡ€ΠΈ создании Ρ‚Π°ΠΊΠΈΡ… ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ² Ρ€Π°Π·ΠΌΠ΅Ρ€ стСка Π±Ρ‹Π» ТСстко Π·Π°ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ Π² исходном ΠΊΠΎΠ΄Π΅, ΠΈ совпадал с ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΌ Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ стСка Π² Node.js (492 ΠšΠ±Π°ΠΉΡ‚ для 32-разрядного процСсса ΠΈ 984 ΠšΠ±Π°ΠΉΡ‚ для 64-разрядного). Π‘ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ Π²Ρ‹ΡΡΠ½ΠΈΠ»ΠΎΡΡŒ, Ρ‡Ρ‚ΠΎ Π½Π΅ всСм Ρ…Π²Π°Ρ‚Π°Π΅Ρ‚ Ρ‚Π°ΠΊΠΎΠ³ΠΎ Ρ€Π°Π·ΠΌΠ΅Ρ€Π°, Π° ΠΊΡ‚ΠΎ-Ρ‚ΠΎ Π½Π°ΠΎΠ±ΠΎΡ€ΠΎΡ‚ Ρ…ΠΎΡ‡Π΅Ρ‚ Π΅Π³ΠΎ ΡƒΠΌΠ΅Π½ΡŒΡˆΠΈΡ‚ΡŒ. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ Π² Ρ‚Ρ€Π΅Ρ‚ΡŒΠ΅ΠΉ вСрсии Π² настройки ΠΌΠΎΠ΄ΡƒΠ»Π΅ΠΉ ChakraCore ΠΈ MSIE Π±Ρ‹Π»Π° Π΄ΠΎΠ±Π°Π²Π»Π΅Π½Π° опция MaxStackSize , с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π·Π°Π΄Π°Ρ‚ΡŒ Ρ€Π°Π·ΠΌΠ΅Ρ€ стСка Π² Π±Π°ΠΉΡ‚Π°Ρ…. По ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Ρ‚ΠΎ ΠΆΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π² стилС Node.js. Если ΠΏΡ€ΠΈΡΠ²ΠΎΠΈΡ‚ΡŒ этому свойству Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Ρ€Π°Π²Π½ΠΎΠ΅ Π½ΡƒΠ»ΡŽ, Ρ‚ΠΎ Π² качСствС максимального Ρ€Π°Π·ΠΌΠ΅Ρ€Π° стСка Π±ΡƒΠ΄Π΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΈΠ· Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ° исполняСмого Ρ„Π°ΠΉΠ»Π°.


Новый ΠΌΠΎΠ΄ΡƒΠ»ΡŒ Π½Π° основС NiL.JS


Π’ Ρ‚Ρ€Π΅Ρ‚ΡŒΠ΅ΠΉ вСрсии появился Π½ΠΎΠ²Ρ‹ΠΉ ΠΌΠΎΠ΄ΡƒΠ»ΡŒ-Π°Π΄Π°ΠΏΡ‚Π΅Ρ€ NiL , ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ создан Π½Π° основС Π΄Π²ΠΈΠΆΠΊΠ° NiL.JS . NiL.JS β€” это Π΅Ρ‰Π΅ ΠΎΠ΄ΠΈΠ½ JS-Π΄Π²ΠΈΠΆΠΎΠΊ, написанный Π½Π° чистом .NET. Π•Π³ΠΎ пСрвая вСрсии Π²Ρ‹ΡˆΠ»Π° Π² 2014 Π³ΠΎΠ΄Ρƒ, Π½ΠΎ ΠΎΠ½ Π½Π΅ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ» Ρ‚Π°ΠΊΡƒΡŽ ΠΏΠΎΠΏΡƒΠ»ΡΡ€Π½ΠΎΡΡ‚ΡŒ ΠΊΠ°ΠΊ Jurassic ΠΈ Jint. Π•Π³ΠΎ основноС достоинство β€” это ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ. Π’ качСствС ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° ΠΌΠΎΠΆΠ½ΠΎ привСсти Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ Ρ‚ΠΎΠ³ΠΎ ΠΆΠ΅ Π±Π΅Π½Ρ‡ΠΌΠ°Ρ€ΠΊΠ°, Ρ‡Ρ‚ΠΎ использовался Π² Ρ€Π°Π·Π΄Π΅Π»Π΅ ΠΏΡ€ΠΎ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΡƒΡŽ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡ†ΠΈΡŽ.


ΠŸΡ€ΠΈ запускС Π² срСдС .NET Framework 4.7.2 ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹:


НазваниС модуляБрСдняя ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚. выполнСнияGen 0 Π½Π° тыс. ΠΎΠΏΠ΅Ρ€.Gen 1 Π½Π° тыс. ΠΎΠΏΠ΅Ρ€.Gen 2 Π½Π° тыс. ΠΎΠΏΠ΅Ρ€.Π’Ρ‹Π΄Π΅Π»Π΅Π½ΠΈΠ΅ памяти Π½Π° ΠΎΠ΄Π½Ρƒ ΠΎΠΏΠ΅Ρ€.
Jint27,19 мс2 812,501 343,75-16 374,58 Кб
Jurassic455,70 мс2 000,001 000,00-15 575,28 Кб
NiL17,80 мс1 000,00--4 424,09 Кб

Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ запуска Π² срСдС .NET Core 2.0 ΠΎΡ‚Π»ΠΈΡ‡Π°ΡŽΡ‚ΡΡ Π½Π΅Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ:


НазваниС модуляБрСдняя ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚. выполнСнияGen 0 Π½Π° тыс. ΠΎΠΏΠ΅Ρ€.Gen 1 Π½Π° тыс. ΠΎΠΏΠ΅Ρ€.Gen 2 Π½Π° тыс. ΠΎΠΏΠ΅Ρ€.Π’Ρ‹Π΄Π΅Π»Π΅Π½ΠΈΠ΅ памяти Π½Π° ΠΎΠ΄Π½Ρƒ ΠΎΠΏΠ΅Ρ€.
Jint24,87 мс2 750,001 375,00-16 300,25 Кб
Jurassic469,97 мс2 000,001 000,00-15 511,70 Кб
NiL19,67 мс1 000,00--4 419,95 Кб

Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ этих тСстов ΠΌΠΎΠΆΠ½ΠΎ Π΄Π°ΠΆΠ΅ Π½Π΅ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ. Π’Π°ΠΊΠΈΠ΅ Π²ΠΏΠ΅Ρ‡Π°Ρ‚Π»ΡΡŽΡ‰ΠΈΠ΅ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ Π±Ρ‹Π»ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Ρ‹ благодаря нСстандартным Ρ€Π΅ΡˆΠ΅Π½ΠΈΡΠΌ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ использовал Π΅Π³ΠΎ Π°Π²Ρ‚ΠΎΡ€ (подробности Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΡƒΠ·Π½Π°Ρ‚ΡŒ ΠΈΠ· Π΅Π³ΠΎ статСй Π½Π° Π₯Π°Π±Ρ€Π΅ ). Π‘Ρ‚ΠΎΠΈΡ‚ Ρ‚Π°ΠΊΠΆΠ΅ Π·Π°ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ Π°Π²Ρ‚ΠΎΡ€Ρ‹ Jint Ρ‚ΠΎΠΆΠ΅ Π½Π΅ сидСли слоТа Ρ€ΡƒΠΊΠΈ ΠΈ с 2016 Π³ΠΎΠ΄Π° Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚ Π½Π°Π΄ Ρ‚Ρ€Π΅Ρ‚ΡŒΠ΅ΠΉ вСрсиСй своСго Π΄Π²ΠΈΠΆΠΊΠ°, ΠΎΠ΄Π½ΠΎΠΉ ΠΈΠ· Π·Π°Π΄Π°Ρ‡ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ являСтся ΠΏΠΎΠ²Ρ‹ΡˆΠ΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ. Если Π²Π΅Ρ€ΠΈΡ‚ΡŒ Π±Π΅Π½Ρ‡ΠΌΠ°Ρ€ΠΊΡƒ для вСрсии 3.0.0 Beta 1353 , Ρ‚ΠΎ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½Π°Ρ вСрсия Jint выполняСт скрипты Π² 2,4 Ρ€Π°Π·Π° быстрСС, Ρ‡Π΅ΠΌ NiL.JS вСрсии 2.5.1200, ΠΈ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚Π΅Π»ΠΈ использования памяти Ρƒ Π½ΠΈΡ… практичСски ΡΠΎΠ²ΠΏΠ°Π΄Π°ΡŽΡ‚.


Π•ΡΡ‚ΡŒ Ρƒ NiL.JS ΠΈ нСдостатки. ВсС ΠΈΠ΄Π΅Ρ‚ Ρ…ΠΎΡ€ΠΎΡˆΠΎ ΠΏΠΎΠΊΠ° ΠΌΡ‹ выполняСм Π½Π° Π½Π΅ΠΌ ΠΊΠ°ΠΊΠΎΠΉ-Ρ‚ΠΎ простой ΠΊΠΎΠ΄, Π½ΠΎ ΠΊΠΎΠ³Π΄Π° ΠΌΡ‹ пытаСмся Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΊΠ°ΠΊΠΈΠ΅-Π½ΠΈΠ±ΡƒΠ΄ΡŒ популярныС Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ, Ρ‚ΠΎ Π½Π°Ρ‡ΠΈΠ½Π°ΡŽΡ‚ Π²ΠΎΠ·Π½ΠΈΠΊΠ°Ρ‚ΡŒ ошибки. Из всСх ΠΌΠΎΠ΄ΡƒΠ»Π΅ΠΉ Bundle Transformer, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‰ΠΈΡ… JavaScript Engine Switcher, ΠΌΠ½Π΅ ΡƒΠ΄Π°Π»ΠΎΡΡŒ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Hogan ΠΈ Handlebars, Ρ‚Π° ΠΆΠ΅ ситуация ΠΈ с ReactJS.NET. Π’ блиТайшСС врСмя я ΡΠΎΠ±ΠΈΡ€Π°ΡŽΡΡŒ Π·Π°Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ всС эти ошибки ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΠΈΡ… Π°Π²Ρ‚ΠΎΡ€Ρƒ NiL.JS.


Links


  1. Π‘Ρ‚Ρ€Π°Π½ΠΈΡ†Π° ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° JavaScript Engine Switcher Π½Π° GitHub
  2. ДокумСнтация JavaScript Engine Switcher
  3. ΠŸΠ΅Ρ€Π΅Π²ΠΎΠ΄ ΠΌΠΎΠ΅Π³ΠΎ поста «ЗаблуТдСния ΠΎ JavaScript Engine Switcher 2.XΒ»
  4. Π‘Ρ‚Ρ€Π°Π½ΠΈΡ†Π° ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° JSPool Π½Π° GitHub
  5. Π‘Π°ΠΉΡ‚ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° ReactJS.NET
  6. Π‘Ρ‚Ρ€Π°Π½ΠΈΡ†Π° ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° ReactJS.NET Π½Π° GitHub



Source: https://habr.com/ru/post/439446/