JavaScript calling functions in another file - javascript

I am aware of import and export, but this doesn't work here because each time you call imported function it starts a new instance of the original file from which it was exported.
I have a JavaScript Code A running in node.js continously without restarting. As it runs, it creates its own data and stores it in arrays.
Please how do I call a function within code A from another file which will run in the same instance Code A is currently running in and that it will respect the stored data in arrays Code A has?
If I use import/export, it calls a function from within Code A, but it doesn't have any stored data in arrays that I gathered whilst running Code A.
Thank you very much.
EDIT: Adding sample code
MAIN FILE
let calculationArray = []
function add(x, y) {
if ( calculationArray.includes("abcde") ) {
console.log("Calculation is on timeout", calculationArray)
}
else {
calculationArray.push("abcde")
console.log(x + y)
return x + y;
}
}
module.exports = { add }
setInterval(add, 3000, 5, 5)
SECOND FILE WHICH CALLS FUNCTION FROM MAIN FILE and doesn't respect the fact that calculationArray in the original file already has "abcde". It simply starts it own fresh instance with empty array
const f = require('./calculation.js');
f.add(10, 5);

I'm not sure how you exactly load and call your scripts... so I tried to make something I could reason about of — out of your code.
Using ES6 modules (which use export and import statements), say you have:
calculation.js — which adds every second 5+5 to a file-internal array
other.js — which imports calculation.js and returns the result of add(100, 100) 200
a main index.js which calls:
calculations.js the first time after 3500ms
other.js after 6500ms
Here are the files and than some explanation on what happens using ES6 dynamic import():
calculation.js
// calculation.js
const calculationArray = [];
const add = (x, y) => {
const result = x + y;
calculationArray.push(result);
console.log(calculationArray.reduce((a, v) => a + v), 0);
return result;
};
// add 5+5 every 1sec
setInterval(add, 1000, 5, 5);
export { add }
other.js
// other.js
import { add } from "./calculation.js";
// export the result of 100 + 100
export default add(100, 100);
and finally:
index.js
// index.js
const delayedImport_calculation = async () => {
const { add } = await import("./calculation.js");
add(10, 5);
};
const delayedImport_other = async () => {
const { default: otherResult } = await import("./other.js");
console.log(otherResult);
};
setTimeout(delayedImport_calculation, 3500);
setTimeout(delayedImport_other, 6500);
If you call the main script like: $ node ./index.js from the terminal, you should expect the following console logs:
(Nothing for 3500ms)
15 after 3500ms since that's when calculation.js was first imported into index.js and called the function add(10, 5);
25 (after 1sec)
35 (also after 1sec since the interval inside calculation.js)
235 since other.js was dynamically imported into index.js and added 100 to the array
other: 200 200 since other.js exports just the result of add(100 + 100)
245, 255, 265, etc... on intervals of 1sec — all proving the point that the array values are updated as expected.
as you can see above, the .reduce() on the calculationArray returns the expected added values in console.log
Another simpler example (which might be closer to what you have?) with only two files:
calculation.js that:
every 1sec adds 5+5 to the array
exports a default add() function and a getAddResult() method
index.js which imports both the default and the helper methods
logs every second the sun of the array values using getAddResult()
calls add(1000, 1000) after 5sec
calculation.js
const calculationArray = [];
const getAddResult = () => calculationArray.reduce((a, v) => a + v, 0);
const add = (x, y) => {
const result = x + y;
calculationArray.push(result);
return result;
};
// add 5+5 every 100ms
setInterval(add, 100, 5, 5);
export default add
export { getAddResult }
index.js
import add, { getAddResult } from "./calculation.js";
setInterval(() => {
console.log(getAddResult())
}, 1000);
setTimeout(() => {
add(1000, 1000);
}, 5000);
the log this time would be approximately like:
90, 180, 280, 370 (in intervals of 1sec)
2470, (after ~5000ms) proving the point that the values in array are updated
2560, 2660, etc... (also in intervals of 1sec)
PS:
for the above to work you either need to use "type": "module" in package.json, or just name all your files extensions as .mjs

Related

React Native: cannot use function as default state hook? issue with array computation and slow UI

I have a function that takes a few discounts and groups them by days, so the final result is an array of 7 representing each day with a few discounts or none in each index, for some reason this computation makes the UI don't feel very snappy, only fully completing when I press the UI and showing an uncompleted result UI before that...
I tried placing a function which gives me the resulting array to the default state of my state hook but I gwt the following error:
getVenueDiscounts is not a function
Before this I was using useEffect with empty array to get my array but as I said it didn't feel very snappy...
Here is my method, maybe the problem is I have not optimized my business logic, is it possible to do this calculations better , maybe with a lodash method?
My method for grouping discounts by week days:
const [ weekDiscounts, setWeekDiscounts ] = useState(getVenueDiscounts());
const getVenueDiscounts = () =>
{
if(discounts.length == 0)
{
var weekDiscountsCopy = [ [], [], [], [], [], [], [] ];
//let weekDiscountsCopy = [...weekDiscounts];
//let response = await venueStore.getVenueDiscounts({ id:1 });
console.log(response.data);
let allDiscounts = _.cloneDeep(venueStore.myDiscounts);
//let allDiscounts = response.data.discounts;
//Alert.alert(response.data.discounts.length+' discounts');
//setDiscounts(discounts);
console.log(response.data.discounts[0].days);
for(var i=0; i<response.data.discounts.length;i++)
{
let discountDays = allDiscounts[i].days;
for(var x=0; x<discountDays.length;x++)
{
if(discountDays[x].id == 1)
{
weekDiscountsCopy[0].push(allDiscounts[i]);
}
else if(discountDays[x].id == 2)
{
weekDiscountsCopy[1].push(allDiscounts[i]);
}
else if(discountDays[x].id == 3)
{
weekDiscountsCopy[2].push(allDiscounts[i]);
}
else if(discountDays[x].id == 4)
{
weekDiscountsCopy[3].push(allDiscounts[i]);
}
else if(discountDays[x].id == 5)
{
weekDiscountsCopy[4].push(allDiscounts[i]);
}
else if(discountDays[x].id == 6)
{
weekDiscountsCopy[5].push(allDiscounts[i]);
}
else if(discountDays[x].id == 7)
{
weekDiscountsCopy[6].push(allDiscounts[i]);
}
}
//setWeekDiscounts(weekDiscountsCopy);
setDiscounts(allDiscounts);
return weekDiscountsCopy;
}
}
};
Thanks in advance!
This is an issue of how and when your code gets interpreted.
Because getVenueDiscounts is a lambda assigned to a const, it gets hoisted to the top of the scope, but remains undefined until that code is actually executed on the second pass -- and the state hook is being initialized with that call before the function has been defined. One solution would be to just move getVenueDiscounts above the hook.
A more detailed description can be found here:
These variable declarations only become initialized when they are evaluated during runtime. The time between these variables being declared and being evaluated is referred to as the temporal dead zone. If you try to access these variables within this dead zone, you will get the reference error above.
Reason of error
Well, you missed on a fundamental concept of programming, that one should not access a variable, function, etc. before initialisation.
MCVE
A minimal, reproducible example of your error would be to try accessing bar before it's initialisation.
const foo = () => {
bar();
^^^^^ // Uncaught ReferenceError: Cannot access 'bar' before initialization
const bar = () => console.log('I worked!');
};
foo();
Solution
There can be two solutions for your problem:
Declare getVenueDiscounts() function before useState() hook.
const getVenueDiscounts = () => {
// logic goes here
}
const [ weekDiscounts, setWeekDiscounts ] = useState(getVenueDiscounts());
Better approach would be to use useMemo() hook which would return a cached value unless the dependencies in the array do not change.
const weekDiscounts = useMemo(() => {
// getVenueDiscounts() logic goes here
},[]); // list of dependencies on which weekDiscounts depends on

How do I test a function that calls another function in jest?

I have a function that converts an array of 8 bit integers into a x-bit integer. This function has extensive unit tests in Jest. This function is called convertArrayIntoInt().
I have another functions that relies on convertArrayIntoInt() called getSignature().
/**
* #param {Uint8Array} fileContents The file contents
* #param {number} position The position in the file where the signature is.
* #return {number} the 32 bit signature
*/
function getSignature(fileContents, position) {
let signature = fileContents.slice(position, position + 4);
signature = convertArrayIntoInt(signature);
for(let s in Enums.Signatures) {
if(signature === parseInt(s)) {
return signature;
}
}
return Enums.Signatures.BAD_SIGNATURE;
}
I want to write a test for this function that tests whether Uint8Array.of(0x04, 0x03, 0x4b, 0x50), 0) returns 0x04034b50 or Enums.BAD_SIGNATURE. I heard that with unit testing, you don't want to test a function that relies on the output of another function. How do I go about doing this in Jest?
Update: Looks like I had some issue in my original response when I merged different files in to a single files I expected it to work as before but it didn't so make sure to put function(s) you want to inject in to separate file(s).
As mentioned earlier, unit test should just test functionality of function it self while mocking dependencies.
I moved functions in to separate files, in one file all functions are defined and in another test.
enums.js
export const Enums = {
Signatures: {
SIGNATURE_1: 1,
SIGNATURE_2: 2,
SIGNATURE_3: 3,
BAD_SIGNATURE: -1
}
};
convertArrayIntoInt.js
export function convertArrayIntoInt(array) {
// processing...
return array.length;
}
getSignature.js
import { convertArrayIntoInt } from "./convertArrayIntoInt";
import { Enums } from "./enums";
export const getSignature = (fileContents, position) => {
let signature = convertArrayIntoInt(
fileContents.slice(position, position + 4)
);
for (let s in Enums.Signatures) {
if (signature === parseInt(s, 10)) {
return signature;
}
}
return signature;
};
tests.spec.js
import { getSignature } from "./getSignature";
import { Enums } from "./enums";
import * as convertArrayIntoIntModule from "./convertArrayIntoInt";
it("Function check", () => {
// arrange
const spy = jest.spyOn(convertArrayIntoIntModule, "convertArrayIntoInt");
spy.mockReturnValue(Enums.Signatures.SIGNATURE_1);
// act
const expected = Enums.Signatures.SIGNATURE_1;
const actual = getSignature([1, 2, 3, 4, 5, 6], 1);
// assert
expect(spy).toBeCalledTimes(1);
expect(actual).toEqual(expected);
// clean up
spy.mockRestore();
});
You can take a look at this sandbox that I created to test dummy function similar to yours.
Yes, you have heard it right. A unit test should only test a single unit/function. What you can do is, you can make another unit test to check convertArrayIntoInt() function, and for this function, you can set dummy return values for convertArrayIntoInt() in order to verify other logic is working correctly or not.
You can spyOn convertArrayIntoInt function and mockReturnValue.
https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname

How can I import a module into an AudioWorkletProcessor that is changed elsewhere?

I'm trying to change a value that is used by an AudioWorkletProcessor from another module, but from the context of the AudioWorkletProcessor, the value doesn't change and just stays the same. But from the module that modifies the data, when queried upon the data has in fact changed there. It's like there is a completely separate instance/state of the module that holds the data (functions.js) for the modifier (main.js) and the reader (audio-processor.js)
Here we have audio-processor.js
import { sawWave } from "/src/functions.js";
var tick = 0
class AudioProcessor extends AudioWorkletProcessor {
process(inputs, outputs, parameters) {
const output = outputs[0]
output.forEach(channel => {
// emphasis on sawWave function
var value = sawWave(tick) * 0.1;
for (let i = 0; i < channel.length; i++) {
channel[i] = value
}
});
tick++
return true
}
}
registerProcessor('audio-processor', AudioProcessor)
Here we have functions.js, which is contains sawWave() which is imported into audio-processor.js
let variables = {
// emphasis on this variable
sawWaveFrequency: 1
}
export function setSawFrequency(freq) {
variables.sawWaveFrequency = freq
}
export function sawWave(tick) {
console.log(variables.sawWaveFrequency)
// this function uses the variable defined above and is imported by audio-processor.js
// variables.sawWaveFrequency doesn't change for audio-processor.js when modified from main.js
// so the output will will always be as if variables.sawWaveFrequency = 1
return tick * variables.sawWaveFrequency % 10;
}
Here we have main.js, which handles input from a HTML page
import { setSawFrequency } from "/src/functions.js";
var audioContext
var whiteNoiseNode
async function init() {
audioContext = new AudioContext()
await audioContext.audioWorklet.addModule("/src/audio-processor.js")
whiteNoiseNode = new AudioWorkletNode(audioContext, 'audio-processor')
}
function play() {
whiteNoiseNode.connect(audioContext.destination)
}
function main() {
document.querySelector("#play").onclick = play;
document.querySelector("#init").onclick = init;
document.querySelector("#slider-mult").oninput = function() {
// this is supposed to change the value, but only does so in the context of main.js and not audio-processor.js
setSawFrequency(this.value);
}
}
main();
EDIT: I take it you're only supposed to use AudioWorkletProcessor.port and the parameters parameter in the process function to communicate with it?
You're already on the right track. To change a value inside your AudioWorkletProcessor you can either use a custom AudioParam or send a message over the MessagePort.
The reason why your code doesn't work is that you technically end up with two instances of the same module. The AudioWorkletProcessor runs on a different thread and has no access to the modules that are loaded on the main thread. Therefore /src/functions.js gets loaded twice. One instance is living on the main thread and the other one is living on the audio thread. Each of them doesn't know that the other exists.
You can also use a SharedArrayBuffer
Create the shared memory object in one module.
const length = 100;
const size = Int32Array.BYTES_PER_ELEMENT * length;
this.sab = new SharedArrayBuffer(size);
this.sharedArray = new Int32Array(this.sab);
Send it to AudioWorkletProcessor
this.worker.port.postMessage({
sab: this.sab
});
In the AudioWorkletProcessor
this.port.onmessage = event => {
this.sharedArray = new Int32Array(event.data.sab);
}
Now both modules share the same array (this.sharedArray).

Class static getter which returns class in Javascript

I have some code:
//Main class
class Tools {
//Subclass implemented by static getter
static get SUPPORTED_UNITS () {
return class {
//Another one
static get Size () {
return class {
//Some data (constants in my case)
};
}
};
}
}
I want to use it like Tools.SUPPORTED_UNITS.Size and I want to receive some data as a result of it.
I use WebStorm for writing JavaScript code, so it shows me this when I tried to use it.
I don't know exactly, maybe I just haven't done some setup to my WebStorm, but.. What I need to do that I can use construction like Class.Subclass.SubClassMethod/SubSubclass.
I need that:
It be not a method
It be static
It be immutable (without Object.freeze or something like that) - so I can't change my Class.Subclass by typing Class.Subclass = foo;
It be indexing by IDE - so I have some help from IDE (like I type Class.Su and she suggest Class.Subclass)
Is it real in ES6 and WebStorm? Or maybe I have to use ES7 and Babel or someting like that?
P.S. That code is working, but I need suggestions from IDE or it will be like a hell.
Following #estus and #Felix Kling suggestion... (likely being misrepresented here)
const TOOLS = {
get SUPPORTED_UNITS() {
return {
get SIZE() {
return {
get Bar() {
return 1;
}
}
}
}
}
};
console.log(TOOLS.SUPPORTED_UNITS.SIZE.Bar) // 1;
TOOLS.SUPPORTED_UNITS.SIZE = "Hello";
// TOOLS.SUPPORTED_UNITS.SIZE = "Hello";
// ^
// TypeError: Cannot set property SIZE of #<Object> which has only a getter
That works but as remarked by #bergi, it creates a new object in every lookup:
console.log(TOOLS.SUPPORTED_UNITS === TOOLS.SUPPORTED_UNITS); // false
Yep...
An alternative still using getters would be encapsulate all those objects in an IIFE:
const TOOLS = (function () {
const SIZE = {
get Bar() { return 1; }
}
const SUPPORTED_UNITS = {
get SIZE() {
return SIZE;
}
}
return {
get SUPPORTED_UNITS() {
return SUPPORTED_UNITS;
}
}
}());
console.log(TOOLS.SUPPORTED_UNITS.SIZE.Bar === TOOLS.SUPPORTED_UNITS.SIZE.Bar); // true
console.log(TOOLS.SUPPORTED_UNITS.SIZE === TOOLS.SUPPORTED_UNITS.SIZE); // true
console.log(TOOLS === TOOLS); // true
TOOLS.SUPPORTED_UNITS = "hello"; // still throws an error
Third update:
The second example solves the issue related to creation of a new object every lookup but it also does not take in consideration that the access to every nested property results in a function call to reevaluate something that is not supposed to change:
//A naive measurement:
for(let counter = 0; counter < 5; counter++) {
let start = Date.now();
for(let i = 0; i < 200000; i++){
let somevar = Tools.SUPPORT_UNITS.SIZE;
}
let end = Date.now();
console.log(`Elapsed using nested getters: ${end - start}ms.`);
}
// Something like:
/** Elapsed using nested getters: 20ms.
* Elapsed using nested getters: 14ms.
* Elapsed using nested getters: 9ms.
* Elapsed using nested getters: 10ms.
* Elapsed using nested getters: 10ms.
*
// While the same as ordinary properties:
* Elapsed ordinary properties: 2ms.
* Elapsed ordinary properties: 5ms.
* Elapsed ordinary properties: 1ms.
* Elapsed ordinary properties: 0ms.
* Elapsed ordinary properties: 0ms.
*/
So it is also better to check alternatives before using this approach if running 200000 times loops is in the foreseeable future.

How can I list all the functions in my node js script?

I've tried looking at global, but it only contains variables, not functions. How can I list all the functions created in my script?
Run node debug from command line with the file you want to look at. Then you can use list(some big number here)
node debug mini_file_server.js
< debugger listening on port 5858
connecting... ok
debug> scripts
26: mini_file_server.js
debug> list(1000)
1 var http = require('http'),
2 util = require('util'),
3 fs = require('fs');
4
5 server = http.createServer(function(req, res){
6 var stream = fs.createReadStream('one.html'),
7 stream2 = fs.createReadStream('two.html');
8 console.log(stream);
9 console.log(stream2);
10 stream.on('end', function(){
11 stream2.pipe(res, { end:false});
12 });
13
14 stream2.on('end', function(){
15 res.end("Thats all!");
16 });
17
18 res.writeHead(200, {'Content-Type' : 'text/plain'});
19 stream.pipe(res, { end:false});
20 stream2.pipe(res, { end:true});
21
22 }).listen(8001);
23 });
debug>
If the function has a name, it'll show up in global just fine:
mb-work-laptop:~ markbessey$ node
> for (var k in global) { console.log(k); }
global
process
GLOBAL
root
Buffer
setTimeout
setInterval
clearTimeout
clearInterval
console
module
require
k
> function z(a) { return a*10; }
> for (var k in global) { console.log(k); }
global
process
GLOBAL
root
Buffer
setTimeout
setInterval
clearTimeout
clearInterval
console
module
require
k
z
>
> global.z
[Function: z]
This is impossible in node without more advanced reflecting tools like the debugger.
The only way to do this would be to use __parent__ which was removed due to security issues and other things. Like Mark Bessey said, when you run the script those variables become module closure variables. You can not access them elsewhere without explicitly exporting them.
This is not a bug, it's by design. It's just how node works. However, if you just ask your users to write function expression assignments, all would work a-ok:
module.exports = {
a:function(){
//same logic you had in the function declaration
}
}
Then, you can easily reflect on and enumerate module.exports and get all the function names.
cli: http://nodejs.org/docs/v0.3.7/api/debugger.html
gui: https://github.com/dannycoates/node-inspector
There's also https://github.com/c4milo/node-webkit-agent in the works which will be a more powerful version of node-inspector.
If you want to do some AOP, the route is AST.
You could build your own AOP framework with something like: http://esprima.org.
Or you could try node-burrito, excellent for not so complex aspects:
var burrito = require('burrito');
var src = burrito('someCall()', function (node) {
if (node.name === 'call') node.wrap('qqq(%s)');
});
will generate
qqq(somecall())
Wanted the exact thing you're looking for. Couldn't find any other solutions (most assumed use of "window" or "this" or "global", none of which will work for us in Node).
We can do it in about 25 lines with the help of a few libraries: fs, esprima, and escodegen.
const fs = require('fs');
const esprima = require("esprima");
const escodegen = require("escodegen");
The logic is this:
Let's take the file this function is in, and first read it as text, just like any other file we would read as plain text
We'll use esprima to parse that text into a valid tree, part of which will contain our functions
We'll filter out that tree to only contain functions (except for this function we're using to do this! We're not after it)
For that, we'll need to grab the easy function declarations
But ideally we can also grab any functions that were declared as variables with arrow expressions, which will take a little more work but is very doable.
Next we want to reconstruct these objects from the tree back into actual usable functions in the code, so for all of our functions:
Use escodegen to reconstruct that object into a string that looks
just like written code for that function
Convert that string into a usable function within the script itself again
The end result will spit back out an object with the key:value pairs where the key is the string of the function's name, and the value is the function itself.
function getAllFunctionsInThisFileExceptThisFunction() {
const thisFunctionName = arguments.callee.name;
const rawTextFromThisFile = fs.readFileSync(__filename, "utf8");
const parsed = esprima.parseScript(rawTextFromThisFile);
const allDeclaredVariables = parsed.body.filter(e=>e.type === "VariableDeclaration");
const allDeclaredFunctions = parsed.body.filter(e=>e.type === "FunctionDeclaration");
let allFunctions = []
for (declaredVariable of allDeclaredVariables){
const declarations = declaredVariable.declarations[0];
if (declarations.init.type === "ArrowFunctionExpression"){
const anonymousFunction = declarations.init;
let reconstructedFunction = anonymousFunction;
reconstructedFunction.id = declarations.id;
allFunctions.push(reconstructedFunction);
}
}
allFunctions.push(...allDeclaredFunctions)
const allFunctionsExceptThisOne = allFunctions.filter(e => e.id.name !== thisFunctionName);
let functionsDict = {};
for (parsedFunction of allFunctionsExceptThisOne) {
const functionString = escodegen.generate(parsedFunction);
const newFunction = eval(`(${functionString})`)
functionsDict[parsedFunction.id.name] = newFunction;
}
return functionsDict;
}
From there you can play away with it like any other object/dictionary.
const allFunctionsDict = getAllFunctionsInThisFileExceptThisFunction();
console.log( allFunctionsDict["sum"](10,30) ) //prints 40
console.log( allFunctionsDict["difference"](350,250) ) //prints 100
console.log( allFunctionsDict["product"](6,4) ) // prints 24
console.log( Object.keys(allFunctionsDict) ) //prints [ 'product', 'sum', 'difference' ]
function sum(a, b) {
return a + b;
}
function difference(a, b) {
return a - b;
}
const product = (a,b) => {
return a * b;
}

Categories

Resources