Combining Functions to be more 'Functional' w/ JavaScript - javascript

I have some (pseudo) code that looks as follows
const { search, hash } = window.location // get search / hash from url
const tokenParts = queryParams(search || hash) // create an object
const { id_token, access_token } = tokenParts // extract from object
const isSessionValid = validateSession(id_token) // check exp time etc and return true / false
if (isSessionValid) {
store('id_token', id_token)
store('access_token', access_token)
window.history.replaceState(null, null, window.location.pathname)
}
I see this pattern a lot in the codebase I am working on, call a method with a value, assign that value to a variable, pass that variable into another method and assign the result to another variable....and so on until you have required value you require to move the program execution on.
From what I have read, functions should really, do-one-thing - rather than these massive, complex beats that can be difficult to test.
My question is, in the case of the pseudo code above, how can this be refactored into a function that returns the result of another function and so on?
I think I need something like
const sessionisValid = validateSession(window.location)
validateSession = ({search, hash}) => (queryParams(search || hash))=> hasTokenExp({id_token})
But I do not understand...
If this is how function programming / composition should work
Is the best approach
If I am just over complicating things

call a method with a value, assign that value to a variable, pass that variable into another method and assign the result to another variable... and so on until you have required value you require to move the program execution on.
This is totally fine. You're building on big function from multiple small functions - exactly how you should do it in functional programming. The variables are just necessary for the wiring.
What you have shown is not a massive, complex beast, it's very clear and clean code. It's easy to test all the individual small functions on their own if you want.
And because all those functions are pure and your variables are immutable, it's really easy to refactor your code from
const { search, hash } = window.location // get search / hash from url
const { id_token, access_token } = queryParams(search || hash)
const isSessionValid = validateSession(id_token) // check exp time etc
if (isSessionValid) {
store('id_token', id_token)
store('access_token', access_token)
window.history.replaceState(null, null, window.location.pathname)
}
to
function getSession({search, hash}) {
const { id_token, access_token } = queryParams(search || hash)
return {
id_token,
access_token,
isSessionValid: validateSession(id_token)
};
}
const { id_token, access_token, isSessionValid } = getSession(window.location);
if (isSessionValid) {
store('id_token', id_token)
store('access_token', access_token)
window.history.replaceState(null, null, window.location.pathname)
}
but unless you can use getSession in multiple places or you need this layer of abstraction for code organisation, the refactoring is unnecessary.
how can this be refactored to use function composition?
It can't really. Function composition works only when the result of one function are fed into another function and nowhere else. But in your code, access_token and id_token are used in multiple places. While it is possible to express this in pointfree style, it's complicated, slow and too abstract. Variables are much easier to use here.
I see this pattern a lot in the codebase I am working on
What exactly is the pattern? Whenever you see duplicated code, you might want to abstract out the common parts. But you need to be evaluate how many common parts and how many distinct parts there are in the code blocks. While always possible, often it's not worth it.

Related

Object scoping rules seem to change due to seemingly irrelevant library?

So, I'm familiar with the general gist of JavaScript's features regarding objects. They're refcounted and if they go to zero, they die. Additionally, apple = banana where both are objects doesn't copy banana to apple but makes apple a reference to banana.
That being said, some of my code has something like this:
// imagine ws require() and setup here...
var RateLimit = require("ws-rate-limit")('10s', 80);
SickWebsocketServer.on("connection", function(mysocket, req){
// blahblahblah...
RateLimit(mysocket); // See below...
mysocket.on("limited", function(){console.log("someone was limited!"});
mysocket.on("message", function(data){
if(JSON.parse(msg).MyFlagToMessageASpecificWebsocketClient){ // obvs dont do this lol
findme = MyArr.find(guy=>guy.Socket==mysocket);
if(findme) console.log("TRIGGER PLS :)"); // GOAL
else console.log("DON'T TRIGGER"); // SOMETHING WENT WRONG
}
});
MyArr.push({MyName:"my SICK object", MyNumber:MyArr.length, Socket:mysocket})
}
The library used for rate limiting is called ws-rate-limit and I have pasted a shortened (non-code removed) version down below (since it's tiny). Imagine it to be in a package called ws-rate-limit (because it is :D).
const duration = require('css-duration')
module.exports = rateLimit
function rateLimit (rate, max) {
const clients = []
// Create an interval that resets message counts
setInterval(() => {
let i = clients.length
while (i--) clients[i].messageCount = 0
}, duration(rate))
// Apply limiting to client:
return function limit (client) {
client.messageCount = 0
client.on('newListener', function (name, listener) {
if (name !== 'message' || listener._rated) return
// Rate limiting wrapper over listener:
function ratedListener (data, flags) {
if (client.messageCount++ < max) listener(data, flags)
else client.emit('limited', data, flags)
}
ratedListener._rated = true
client.on('message', ratedListener)
// Unset user's listener:
process.nextTick(() => client.removeListener('message', listener))
})
// Push on clients array, and add handler to remove from array:
clients.push(client)
client.on('close', () => clients.splice(clients.indexOf(client), 1))
}
}
My issue is that, when I do use the RateLimit function, the "DON'T TRIGGER" code triggers. If I literally remove that one single line (RateLimit(mysocket)) it goes into "TRIGGER PLS :)".
The above is obviously logically simplified from my actual application but I think you get the gist. Apologies for any misspellings that may lead to undefineds or stuff like that; I promise you my code works if not for the RateLimit(mysocket) line.
When I add console.logs into the find function to log both the guy.Socket object and the mysocket object, with the RateLimit(mysocket) line, the mysocket object's .toString() returns [object global] rather than [object Object]. I know that this is some complicated JavaScript object scoping problem, but I have no clue where to start in terms of investigating it.
Thank you! :)
I'll take a random shot in the dark based on intuition. My best guess is that your issue is with the guy.Socket==mysocket line. Comparing objects that way will only check if they both point to the same heap memory location, even if it's two different stack variables. In your example I can only assume that the RateLimit(mysocket) line is somehow creating a new heap location for that stack variable (creating a new object from it) and because of that your == comparison is then no longer equal (even if they have the exact same values) because they're pointing to different locations.
Try using: JSON.stringify(guy.socket) === JSON.stringify(mysocket).

More efficient way to handle an extremely specific response from a Rest API in JavaScript

I'm working with a Dungeons & Dragons 5e API and want that an especific result be treated in a special way. The user can choose what to search from a range of options, and in only one of them would I need to take care of the answer in a different way. In this option, I get the answer in JSON that contains a 'name' field, which stores a String, but in this specific case this String comes with an acronym, and I would like to transform it into the full name.I'm afraid to just put am 'if' statement in the middle of the code and deal with the situation inefficiently, even more so that I did not find similar situations to have any reference.
This is part of the result of the API I want to handle in a special way:
{"count":6,
"results":[
{"name":"STR",
"url":"http://www.dnd5eapi.co/api/ability-score/1"},
{"name":"DEX",
"url":"http://www.dnd5eapi.co/api/ability-scores2"},
....
]
}
This is how I handle the answer:
fetch(fullAPIURL)
.then(result => result.json())
.then(data => {
let resultContainer = document.getElementById('resultContainer');
//Cleaning the result container from previous results
document.querySelectorAll('#resultContainer article').forEach(container =>
resultContainer.removeChild(container));
spanSearchResult.classList.remove('invisible', 'searchFail');
spanSearchResult.classList.add('searchSucess');
spanSearchResult.innerHTML = `Search returned ${data.count} results`;
for (element of data.results) {
let containerTitle = element.name != undefined ? element.name : element.class;
resultContainer.appendChild(createResultContainer(containerTitle));
}
})
.catch(err => {
spanSearchResult.classList.remove('invisible');
spanSearchResult.classList.add('searchFail');
spanSearchResult.innerHTML = 'Something went wrong! Details in the console';
console.log(err);
});
Is putting a condition in this snippet of code really the most efficient way to solve this situation?
Thanks in advance.
You could just make a lookup call, actually. In fact, that'd be preferable if you ever want to port your application to another language, for example.
Define the following:
var retrieve = (function() {
var items = {
"STR": "Strength",
"DEX": "Dexterity"
};
return function(item) {
return items[item] || item;
}
})();
console.log(retrieve("DEX"));
With this, you can simply call retrieve(element.name) to retrieve its "actual" name. You can add elements to the object to create new translations, and if you ever need to support multiple languages, you can even replace the function entirely.

JSON.stringify is very slow for large objects

I have a very big object in javascript (about 10MB).
And when I stringify it, it takes a long time, so I send it to backend and parse it to an object( actually nested objects with arrays), and that takes long time too but it's not our problem in this question.
The problem:
How can I make JSON.stringify faster, any ideas or alternatives, I need a javaScript solution, libraries I can use or ideas here.
What I've tried
I googled a lot and looks there is no better performance than JSON.stringify or my googling skills got rusty!
Result
I accept any suggestion that may solve me the long saving (sending to backend) in the request (I know its big request).
Code Sample of problem (details about problem)
Request URL:http://localhost:8081/systemName/controllerA/update.html;jsessionid=FB3848B6C0F4AD9873EA12DBE61E6008
Request Method:POST
Status Code:200 OK
Am sending a POST to backend and then in JAVA
request.getParameter("BigPostParameter")
and I read it to convert to object using
public boolean fromJSON(String string) {
if (string != null && !string.isEmpty()) {
ObjectMapper json = new ObjectMapper();
DateFormat dateFormat = new SimpleDateFormat(YYYY_MM_DD_T_HH_MM_SS_SSS_Z);
dateFormat.setTimeZone(TimeZone.getDefault());
json.setDateFormat(dateFormat);
json.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
WebObject object;
// Logger.getLogger("JSON Tracker").log(Level.SEVERE, "Start");
try {
object = json.readValue(string, this.getClass());
} catch (IOException ex) {
Logger.getLogger(JSON_ERROR).log(Level.SEVERE, "JSON Error: {0}", ex.getMessage());
return false;
}
// Logger.getLogger("JSON Tracker").log(Level.SEVERE, "END");
return this.setThis(object);
}
return false;
}
Like This
BigObject someObj = new BigObject();
someObj.fromJSON(request.getParameter("BigPostParameter"))
P.S : FYI this line object = json.readValue(string, this.getClass());
is also very very very slow.
Again to summarize
Problem in posting time (stringify) JavaScript bottle nick.
Another problem parsing that stringified into an object (using jackson), and mainly I have svg tags content in that stringified object as a style column, and other columns are strings, int mainly
As commenters said - there is no way to make parsing faster.
If the concern is that the app is blocked while it's stringifying/parsing then try to split data into separate objects, stringily them and assemble back into one object before saving on the server.
If loading time of the app is not a problem you could try to ad-hoc incremental change on top of the existing app.
... App loading
Load map data
Make full copy of the data
... End loading
... App working without changes
... When saving changes
diff copy with changed data to get JSON diff
send changes (much smaller then full data)
... On server
apply JSON diff changes on the server to the full data stored on server
save changed data
I used json-diff https://github.com/andreyvit/json-diff to calc changes, and there are few analogs.
Parsing is a slow process. If what you want is to POST a 10MB object, turn it into a file, a blob, or a buffer. Send that file/blob/buffer using formdata instead of application/json and application/x-www-form-urlencoded.
Reference
An example using express/multer
Solution
Well just as most big "repeatable" problems go, you could use async!
But wait, isn't JS still single-threaded even when it does async... yes... but you can use Service-Workers to get true async and serialize an object way faster by parallelizing the process.
General Approach
mainPage.js
//= Functions / Classes =============================================================|
// To tell JSON stringify that this is already processed, don't touch
class SerializedChunk {
constructor(data){this.data = data}
toJSON() {return this.data}
}
// Attach all events and props we need on workers to handle this use case
const mapCommonBindings = w => {
w.addEventListener('message', e => w._res(e.data), false)
w.addEventListener('error', e => w._rej(e.data), false)
w.solve = obj => {
w._state && await w._state.catch(_=>_) // Wait for any older tasks to complete if there is another queued
w._state = new Promise((_res, _rej) => {
// Give this object promise bindings that can be handled by the event bindings
// (just make sure not to fire 2 errors or 2 messages at the same time)
Object.assign(w, {_res, _rej})
})
w.postMessage(obj)
return await w._state // Return the final output, when we get the `message` event
}
}
//= Initialization ===================================================================|
// Let's make our 10 workers
const workers = Array(10).fill(0).map(_ => new Worker('worker.js'))
workers.forEach(mapCommonBindings)
// A helper function that schedules workers in a round-robin
workers.schedule = async task => {
workers._c = ((workers._c || -1) + 1) % workers.length
const worker = workers[workers._c]
return await worker.solve(task)
}
// A helper used below that takes an object key, value pair and uses a worker to solve it
const _asyncHandleValuePair = async ([key, value]) => [key, new SerializedChunk(
await workers.schedule(value)
)]
//= Final Function ===================================================================|
// The new function (You could improve the runtime by changing how this function schedules tasks)
// Note! This is async now, obviously
const jsonStringifyThreaded = async o => {
const f_pairs = await Promise.all(Object.entries(o).map(_asyncHandleValuePair))
// Take all final processed pairs, create a new object, JSON stringify top level
final = f_pairs.reduce((o, ([key, chunk]) => (
o[key] = chunk, // Add current key / chunk to object
o // Return the object to next reduce
), {}) // Seed empty object that will contain all the data
return JSON.stringify(final)
}
/* lot of other code, till the function that actually uses this code */
async function submitter() {
// other stuff
const payload = await jsonStringifyThreaded(input.value)
await server.send(payload)
console.log('Done!')
}
worker.js
self.addEventListener('message', function(e) {
const obj = e.data
self.postMessage(JSON.stringify(obj))
}, false)
Notes:
This works the following way:
Creates a list of 10 workers, and adds a few methods and props to them
We care about async .solve(Object): String which solves our tasks using promises while masking away callback hell
Use a new method: async jsonStringifyThreaded(Object): String which does the JSON.stringify asynchronously
We break the object into entries and solve each one parallelly (this can be optimized to be recursive to a certain depth, use best judgement :))
Processed chunks are cast into SerializedChunk which the JSON.stringify will use as is, and not try to process (since it has .toJSON())
Internally if the number of keys exceeds the workers, we round-robin back to the first worker and overschedule them (remember, they can handle queued tasks)
Optimizations
You may want to consider a few more things to improve performance:
Use of Transferable Objects which will decrease the overhead of passing objects to service workers significantly
Redesign jsonStringifyThreaded() to schedule more objects at deeper levels.
You can explore libraries like fast-json-stringify which use a template schema and use it while converting the json object, to boost the performance. Check the below article.
https://developpaper.com/how-to-improve-the-performance-of-json-stringify/

Call Functions Based On Query String Using JavaScript

On my web app, the user is asked a question and can choose only one of two answers. Yes or no. A query string is created based on their answer.
The following code carries the query string through the URL of every page:
var navlinks = document.getElementsByClassName("qString");
$(navlinks).prop("href", function() { return this.href + location.search; })
There are only 2 query strings, ?choice=yes and ?choice=no.
Once the user is taken through the app, if they navigate to either park01.html, park02.html, or park03.html from any other page, data will be pulled accordingly via a called function().
Here's my concept in pseudocode:
// I assume I should store specific html pages to a variable
var parkPages = ["park01.html", "park02.html", "park03.html”];
if (user clicks on specified html pages stored in variable) {
and the url contains = ?choice=yes;
Then call these functions: funcA(), funcB(), funcC();
}
else {
the url contains = ?choice=no;
Then call these functions: funcD(), funcE(), funcF();
}
Does the concept make sense? And what does the syntax look like?
If you're simply looking for a concrete translation of your pseudocode into JavaScript, based on your last comment, this should be what you need:
if (location.search === "?choice=yes") {
funcA();
funcB();
funcC();
}
else {
funcD();
funcE();
funcF();
}
Though at this stage, I'd recommend spending less time here and more on instructional/tutorial based websites.

node.js/javascript/couchdb view to associative array does not seem to work

I am trying to create a webapp on a node/couchdb/windows stack but get terribly stung by what seems to be a lack of experience.
In the database, there is a view that returns all users with passwords. Based on the tutorial for a blog I have tried to access the view through my node code.
Whenever I investigate the structure of the users or users variable, I get an undefined object.
The call to getDatabase() has been tested elsewhere and works at least for creating new documents.
function GetUser(login)
{
var users = GetUsers();
return users[login];
}
function GetUsers() {
var db = getDatabase();
var usersByEmail = [];
db.view("accounts", "password_by_email")
.then(function (resp) {
resp.rows.forEach(function (x) { usersByEmail[x.key] = x.value});
});
//usersByEmail['test'] = 'test';
return usersByEmail;
}
I am aware that both the use of non-hashed passwords as well as reading all users from the database is prohibitive in the final product - just in case anyone wanted to comment on that.
In case something is wrong with the way I access the view: I am using a design document called '_design/accounts' with the view name 'password_by_email'.
Your call to db.view is asynchronous, so when you hit return usersByEmail the object hasn't yet been populated. You simply can't return values from async code; you need to have it make a callback that will execute the code that relies on the result.

Categories