obscurejavascript
obscurejavascript
Obscure JavaScript
310 posts
A blog about the obscure and interesting bits of JavaScript. Some of it may be useful, most of it will probably just be entertaining. Updated weekly.
Don't wanna be here? Send us removal request.
obscurejavascript 5 years ago
Text
Making Objects Immutable in JavaScript
If the below code blocks do not show up properly due to a recent Tumblr change. View them directly at https://obscurejavascript.tumblr.com/
By default any user created in JavaScript can have most of its properties modified. In some cases this can lead to confusing errors like a config setting object being updated via event driven code in a very hard to track down way. To avoid this property modifications of any kind can be prevented.
Firstly here is an example of the function in action:
const config = immutable({ id: 'test-1', adminUsers: [ { id: 'user-1', name: 'User 1' }, { id: 'user-2', name: 'User 2' }, { id: 'user-3', name: 'User 3' } ], }); // Simulate some event driven code doing an update setTimeout(() => { // This will either give an error in strict mode or newer environments or just // fail to do anything silently in other situations. config.adminUsers[1].name = 'User 2+'; }, 200); // Simulate some event driven code checking the config setTimeout(() => { console.log('User 2:', config.adminUsers[1]); }, 500);
This is the immutable:
function immutable(obj) { // Simple implementation to demonstrate the point. This is just example code. for (const property in obj) { if (obj.hasOwnProperty(property)) { const value = obj[property]; if (Array.isArray(value) || typeof value === 'object') { obj[property] = immutable(value); } } } return Object.freeze(obj); }
The immutable function simply goes through each property of an object and then freezes it. Note that Object.freeze will only freeze the top level properties. To freeze sub objects, the object property values need to be looped through. Since the above is just example code, it only goes deeper when an array or basic object of some form is encountered. Some edge cases may be missed.
I find this type of function very useful when trying to track down hard to follow modifications. I just set it on an object that gets modified in a hard to track down way and then a stack trace gives me exactly where that happened.
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2020/immutable.js
23 notes View notes
obscurejavascript 5 years ago
Text
Optional Chaining in JavaScript
If the below code blocks do not show up properly due to a recent Tumblr change. View them directly at https://obscurejavascript.tumblr.com/
Just like with optional function calls, it is possible to use a simple syntax for optional chaining. Firstly, without a library or helper method, getting a deep property safely can involve a lot of checks:
function printShortUrls(menu = []) { for (const option of menu) { if (option) { if (option.location && option.location.shortUrl) { console.log(option.title, ':', option.location.shortUrl); } else { console.log(option.title, ': (none)'); } } else { console.log('(empty)'); } } } const menu = [ { title: 'Dashboard', location: { fullUrl: 'https://my-website.com/dashboard/user1', shortUrl: 'https://goo.gl/929okH' } }, null,// Used to mark a menu separator { title: 'Logout', // No location since it redirects to a login page } ]; printShortUrls(menu); // Dashboard : https://goo.gl/929okH // (empty) // Logout : (none)
By using optional chaining, this can be simplified a lot:
function printShortUrls2(menu = []) { for (const option of menu) { if (option) { const shortUrl = option?.location?.shortUrl || '(none)'; console.log(option.title, ':', shortUrl); } else { console.log('(empty)'); } } }
Optional chaining is now supported in all major browsers. Though only very recently, so make sure you are on the latest version if running the code above on one of them. It is already in Node.js 14, but that version will not be ready for production use (for most people) until about October this year.
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2020/optionalChaining.js
5 notes View notes
obscurejavascript 5 years ago
Text
Converting Class Specific Functions Into General Ones In JavaScript
If the below code blocks do not show up properly due to a recent Tumblr change. View them directly at https://obscurejavascript.tumblr.com/
Sometimes functions in classes are useful beyond just a class. Even if they do not determine their results compeletly based on the arguments passed in, they usually can be extracted. Though sometimes this functionality is too complex and modifying it is too risky, especially in legacy code. Here is a simple example, in a real work scenario the main function would be much more complicated:
class Car { constructor(name, hp, weight) { this.name = name; this.hp = hp; this.weight = weight; this.setPowerToWeightRatio(); } setPowerToWeightRatio() { this.powerRatio = Math.round((this.hp/this.weight) * 100) + '%'; } // Complex functionality that also uses setPowerToWeightRatio() here } const miata = new Car('miata', 155, 2387); console.log(miata.powerRatio); // 6%
Assuming that calculatePowerToWeightRatio cannot be modified since other parts of the code rely on it, then its functionality can still be extracted. By a collecting its this settings and then returning those:
function powerToWeightRatio(hp, weight) { const collector = { hp, weight }; Car.prototype.setPowerToWeightRatio.call(collector); return collector.powerRatio; } console.log(powerToWeightRatio(1000, 1000)); // 100% console.log(powerToWeightRatio(500, 1000)); // 50%
Notice how you do not even need an instantiated object to extract the function. Just having a reference to the class works. I am calling this using a Collector since I could find no reference to this tactic on the internet (probably because it is very niche). So by using a Collector, the setPowerToWeightRatio method can be used across the code in general without having modified the original one or having copied any code.
Finally, it is best to avoid coding in the way of the original class when possible. At the very least, instead of using built in properties it is more flexible to pass in properties. Additionally, if a value is returned instead of set internally, it will make the method much more flexible without much extra work. The above tactic is used to work with legacy code (e.g. large libraries) that you do not have time to modify in the short term.
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2020/collector.js
7 notes View notes
obscurejavascript 5 years ago
Text
Optional Function Calls In JavaScript
If the below code blocks do not show up properly due to a recent Tumblr change. View them directly at https://obscurejavascript.tumblr.com/
This is useful when using a Publisher/Subscriber pattern or similar. Which is useful for making 2 parts of code communicate when one is loaded before another (e.g. they are both loaded asynchronously). Additionally, those 2 parts of code only need to know about the central element and not each other making it easier to modify one without affecting the other. Firstly, here is a very simple example:
class Mailbox { subscriber = null; register(subscriber) { this.subscriber = subscriber; } publish(message) { if (typeof this.subscriber === 'function') { this.subscriber(message); } } } const mailbox = new Mailbox(); // Keep on publishing messages let number = 1; setInterval(() => { number += 1; mailbox.publish(`Test Message: ${number}`); }, 500) mailbox.register((message) => { console.log('message:', message); }); // message: Test Message: 2 // message: Test Message: 3 // message: Test Message: 4 // and so on...
Notice how the first message was not printed ("Test Message: 1") since the subscriber was not yet available. Which is to simulate when a publisher starts before a subscriber. To handle this a specific function call check needs to be made for subscriber existence. Instead, the original subscriber value could be set to a function. But then it would be hard to determine when a subscriber was set externally to the Mailbox.
Anyways, if optional chaining is supported in your environment, the call can be simplified:
publish(message) { this.subscriber?.(message); }
Optional chaining is currently supported in Chrome, Firefox and a few other environments but not Node.js. Since this is becoming part of the JavaScript standard, it should be in all environments relatively soon.
Github Location: https://github.com/Jacob-Friesen/obscurejs/blob/master/2020/optionalFunctionCalls.js
4 notes View notes
obscurejavascript 5 years ago
Text
The new.target Property And Why It Is Useful In JavaScript
If the below code blocks do not show up properly due to a recent Tumblr change. View them directly at https://obscurejavascript.tumblr.com/
new.target is a property of an object corresponding to the current instance scope. This is useful to check whether an object was created with new or not. Firstly, not instantiating with new can have confusing outcomes:
'use strict'; function Car() { return this; } Car.prototype.setName = function(name) { this.name = name; }; Car.prototype.getName = function() { return this.name; }; const miata = new Car(); miata.setName('miata'); console.log(miata.getName()); // miata const ferrari = Car(); ferrari.setName('ferrari'); // TypeError: Cannot read property 'setName' of undefined
Basically, when not using new the class function is called like a normal function and does not instantiate an object. So this is undefined. If the return is removed, the result will still be the same since using new will always return an instance of an object.
Anyways, I did not use the new class syntax deliberately. When it is used instead an error will be thrown when Car is called without using new:
class Car { setName(name) { this.name = name; } getName() { return this.name; } } const miata = new Car(); miata.setName('miata'); console.log(miata.getName()); // miata const ferrari = Car(); // TypeError: Class constructor Car cannot be invoked without 'new'
Which is one reason to use the new syntax. But in some cases you will encounter legacy code not in the class syntax that may need to be used that cannot be refactored easily (e.g. complex logic with no tests). So new.target can be used to add that much less confusing class error message without having to convert everything:
function Car() { if (!new.target) { throw(new TypeError("Class constructor Car cannot be invoked without 'new'")); } } Car.prototype.setName = function(name) { this.name = name; }; Car.prototype.getName = function() { return this.name; }; const miata = new Car(); miata.setName('miata'); console.log(miata.getName()); // miata const ferrari = Car(); // TypeError: Class constructor Car cannot be invoked without 'new'
This way JS provides a way of replicating the more strict nature of the class syntax while maintaining strict backwards compatability with the old function syntax based class creation.
Unfortunately, since this is undefined in the above case when called without new the name of the class cannot be automatically determined. So it must be manually given for each legacy syntax class this is done for.
new.target works in all modern browsers
Github Location: https://github.com/Jacob-Friesen/obscurejs/blob/master/2020/newTarget.js
4 notes View notes
obscurejavascript 5 years ago
Text
Private Methods in JavaScript (New Syntax)
If the below code blocks do not show up properly due to a recent Tumblr change. View them directly at https://obscurejavascript.tumblr.com/
Just like private variables, private methods have always been possibly in JavaScript though there syntax is even more cumbersome:
function Car(name, hp) { function getSecret() { return 'test 1'; } this.name = name; this.hp = hp; // Note this will not appear in inherited copies of this object. this.print = function() { console.log(`${this.name} (${this.hp}) - secret: ${getSecret()}`); } } const miata = new Car('miata', 155); console.log(miata.name); // miata console.log(miata.hp); // 155 console.log(miata.getSecret); // undefined miata.print(); // miata (155) - secret: test1
In other words functions only available in a scope no longer accessible need to be referenced. Unlike with private class fields, there is no direct equivalent to using # in modern browsers like this documentation describes. Though something very similar can be done:
class Car { #getSecret = function() { return 'test 1'; } constructor(name, hp) { this.name = name; this.hp = hp; } print() { console.log(`${this.name} (${this.hp}) - secret: ${this.#getSecret()}`); } } const miata = new Car('miata', 155); console.log(miata2.name); // miata console.log(miata2.hp); // 155 console.log(miata.getSecret); // undefined miata2.print(); // miata (155) - secret: test1
Basically, just use a private class field and assign a function to it directly. That function even has access to the current class this as long as a normal function is used and not an arrow function. Since this uses a private field, this is currently supported in Node.js 12 and Chrome by default. It will likely be in other browsers soon since it is now part of the JS standard.
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2020/privateVariables.js
8 notes View notes
obscurejavascript 5 years ago
Text
Private Variables in JavaScript (New Syntax)
Private variables have always been possible in JavaScript, but before the new syntax they required somewhat cumbersome steps to use. For example:
function Car(name, hp) { const privateVariable = 'test1'; this.name = name; this.hp = hp; // Note this will not appear in inherited copies of this object. this.print = function() { console.log(`${this.name} (${this.hp}) - secret: ${privateVariable}`); } } const miata = new Car('miata', 155); console.log(miata.name); // miata console.log(miata.hp); // 155 console.log(miata.privateVariable); // undefined miata.print(); // miata (155) - secret: test1
Basically, private variables can be created in a scope that is no longer available and used via a closure. Now with the # syntax, this is much more straightforward:
class Car { #privateVariable = 'test1'; constructor(name, hp) { this.name = name; this.hp = hp; } print() { console.log(`${this.name} (${this.hp}) - secret: ${this.#privateVariable}`); } } const miata = new Car('miata', 155); console.log(miata.name); // miata console.log(miat2.hp); // 155 console.log(miata.privateVariable); // undefined miata.print(); // miata (155) - secret: test1
This is currently supported in Node.js 12 and Chrome by default. It will likely be in other browsers soon since it is now part of the JS standard.
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2020/privateVariables.js
23 notes View notes
obscurejavascript 5 years ago
Text
Checking For Property Existence In JavaScript
In JavaScript code, when checking for a property sometimes a truthy check or undefined check is incorrectly used since they work in most cases. For example, for the truthy check (The correct output should only give "other" as false):
let weight; const car = { name: 'miata', hp: 0, weight, }; console.log('Has "name":', car.name ? true : false); // Has "name": true console.log('Has "hp":', car.hp ? true : false); // Has "hp": false console.log('Has "weight":', car.weight ? true : false); // Has "weight": false console.log('Has "other":', car.other ? true : false); // Has "other": false
For the undefined check:
console.log('Has "name":', car.name !== undefined); // Has "name": true console.log('Has "hp":', car.hp !== undefined); // Has "hp": true console.log('Has "weight":', car.weight !== undefined); // Has "weight": false console.log('Has "other":', car.other !== undefined); // Has "other": false
Instead the in operator should be used directly in this case:
console.log('Has "name":', 'name' in car); // Has "name": true console.log('Has "hp":', 'hp' in car); // Has "hp": true console.log('Has "weight":', 'weight' in car); // Has "weight": true console.log('Has "other":', 'other' in car); // Has "other": false
Using in also ensures that property deletion is separated from reassignment. Since the only way to delete a property is to use the delete keyword explicitly:
delete car.hp; console.log('Has "name":', 'name' in car); // Has "name": true console.log('Has "hp":', 'hp' in car); // Has "hp": false console.log('Has "weight":', 'weight' in car); // Has "weight": true console.log('Has "other":', 'other' in car); // Has "other": false
Finally, it is important to keep in mind in will check all properties in an object including ones inherited from parent objects. Usually this is what is intended, but if not the Object.hasOwnProperty() method can be used
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2020/in.js
8 notes View notes
obscurejavascript 5 years ago
Text
Nullish Coalescing Operator In JavaScript
A short way of setting default values in JavaScript is using the logical or operator ||. Which returns the left value if the left value is truthy. Otherwise the right value is returned. For example:
function message(contents) { console.log('Message:', String(contents || '(No Message)')); } message(); // Message: (No Message) message('Some contents'); // Message: Some contents
This is supported by all JavaScript environments and browsers even very old ones. Unlike more modern syntax like default function parameters. Note that strings can be created with new String(), which is why the main statement is wrapped in String() above:
message(new String('Some contents')); // Message: Some contents
Anyways, this relies on a value being truthy which is a concept with a bunch of strange exceptions that programmers often miss unless being very careful: https://developer.mozilla.org/en-US/docs/Glossary/Falsy. For example, if I were to pass an empty string in the below:
message(''); // Message: (No Message)
Which is wrong, it should print "Message: ". This is because unlike all other strings an empty string is falsy. So instead of using || more explicity emptyiness checks should be done. So making message work in all cases (assuming the value passed in is always a string):
function message(contents) { console.log('Message:', String((contents || contents === '') ? contents : '(No Message)')); } message(); // Message: (No Message) message('Some contents'); // Message: Some contents message(''); // Message: message(new String()); // Message: message(new String('Some contents')); // Message: Some contents
Which is getting pretty complicated for such a simple operation. Since the above is kind of painful to figure out the danger is there is the temptation to only use || since it works in almost every case. Instead the nullish coalescing operator can be used ??:
function message(contents) { console.log('Message:', String(contents ?? '(No Message)')); }
Currently, this is only fully supported in the latest Chrome, Firefox and a few other environments. Though it will be implemented in other JS environments relatively soon since it is becoming part of the JavaScript standard: https://github.com/tc39/proposal-nullish-coalescing. You can read about the tc39 process here: https://2ality.com/2015/11/tc39-process.html
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2020/nullishOperator.js
5 notes View notes
obscurejavascript 5 years ago
Text
Public Instance Fields For Classes In JavaScript
Previously, to declare class fields you had to put them in some kind of method in the class (usually the constructor). For example:
class Car { constructor() { this.name = 'Not Set'; this.hp = 0; } format() { return `${this.name} - ${this.hp}`; } } const defaultCar = new Car(); console.log(defaultCar.format()); // Not Set - 0 const miata = new Car(); miata.name = 'miata'; miata.hp = 155; console.log(miata.format()); // miata - 155
With public instance fields the constructor can be removed as well as some of the boilerplate code:
class Car { name = 'Not Set'; hp = 0; format() { return `${this.name} - ${this.hp}`; } }
This makes the type of the field clearer as well as ensuring the core API of the class is defined at the top. This also makes it less liekly to forget to initialize defaults for fields.
Public instance fields are supported in Node.js 12 and the latest Chrome and Firefox. Other modern browser support should come this year.
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2020/publicInstanceFields.js
8 notes View notes
obscurejavascript 5 years ago
Text
Uniq By In JavaScript
uniqBy loops through a given array and makes it unique by checking the returned value. For example, it can be used to find non duplicate entries by the name property:
const cars = [ { name: 'miata', hp: 155, weight: 2400 }, { name: '4c', hp: 237, weight: 2465 }, { name: '4c', hp: 237, weight: 2465 }, { name: 'miata', hp: 155, weight: 2400 }, { name: 'elise', hp: 217, weight: 2000 }, { name: '4c', hp: 237, weight: 2465 }, ]; function uniqBy(arr, callback) { const mapObj = {}; const uniques = []; for (const ele of arr) { const result = callback(ele); if (mapObj[result] !== true) { mapObj[result] = true; uniques.push(ele); } } return uniques; } const uniqueCars = uniqBy(cars, (car) => car.name); console.log(uniqueCars); // [ { name: 'miata', hp: 155, weight: 2400 }, // { name: '4c', hp: 237, weight: 2465 }, // { name: 'elise', hp: 217, weight: 2000 } ]
This is more useful than a map of a single property since now whole objects can be used from that unique list. That uniqBy was just for demonstration purposes though. Most libraries will implement a more complete function. For example, the Lodash uniqBy contains alot of optimizations based on the size of the array and other data characteristics.
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2020/uniqBy.js
5 notes View notes
obscurejavascript 5 years ago
Text
Waiting For the First Returned Or Errored API Result With Promise.race() in JavaScript
Promise.race resolves with the found value when the first promise successfully resolves or errors. Compared to Promise.any or Promise.all, the use cases are more niche. One case is checking how a given set of APIs from a server can return and you don't know if the call will give an error or not. Though the primary purpose to know about Promise.any is to know how it fits into the 4 Promise Array methods.
The simplest way to explain it is in relation to Promise.any which returns only on the first successful result. For example, the first request below will end the waiting even though it was an error:
if (typeof Promise.race !== 'function') { Promise.race = function(promises) { let fulfilledOrRejected = false; return new Promise((resolve, reject) => { for (const promise of promises) { promise.then((result) => { if (fulfilledOrRejected !== true) { fulfilledOrRejected = true; resolve(result); } }).catch((err) => { if (fulfilledOrRejected !== true) { fulfilledOrRejected = true; reject(err); } }); } }); } } function apiA() { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ some: 'data', from: 'A' }); }, 1000); }); } function apiB() { return new Promise((resolve, reject) => { setTimeout(() => { reject({ some: 'data', from: 'B' }); }, 200); }); } function apiC() { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ some: 'data', from: 'C' }); }, 1500); }); } async function getData() { console.time('promise.race completion time'); try { const data = await Promise.race([ apiA(), apiB(), apiC(), ]); console.timeEnd('promise.race completion time'); // (Does not reach here) } catch (err) { console.info('Error:', err); console.timeEnd('promise.race completion time'); // Error: { some: 'data', from: 'B' } // promise.race completion time: 211.537ms } } getData();
Whereas Promise.any would end later with apiA. The implementation is very similar to Promise.any in that previous post, but just calls the callback for either success or failure:
if (typeof Promise.race !== 'function') { Promise.race = function(promises) { let fulfilledOrRejected = false; return new Promise((resolve, reject) => { for (const promise of promises) { promise.then((result) => { if (fulfilledOrRejected !== true) { fulfilledOrRejected = true; resolve(result); } }).catch((err) => { if (fulfilledOrRejected !== true) { fulfilledOrRejected = true; reject(err); } }); } }); } }
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2020/promiseRace.js
1 note View note
obscurejavascript 5 years ago
Text
Promise Array Methods in JavaScript
It can be difficult to remember the usecases for all the different types of array based Promise methods when most of their functionality is similar (but still different in important ways). So here is a table of the features and levels of browser support:
Promise.any() Resolves when any promise is successful (resolved) Browser/Node.js Support Minimal Link https://obscurejavascript.tumblr.com/post/189814490444/waiting-for-the-first-returned-api-result-with
Promise.race() Resolves when any promise is resolved or rejected (e.g. an error occured) Browser/Node.js Support All Modern Link https://obscurejavascript.tumblr.com/post/190221199570/waiting-for-the-first-returned-or-errored-api
Promise.all() Resolves when all promises are resolved successfully Browser/Node.js Support All Modern Link https://obscurejavascript.tumblr.com/post/184347636679/waiting-for-all-promises-to-execute-in-javascript
6 notes View notes
obscurejavascript 5 years ago
Text
Best Posts of 2019
Each year I make a list of the best posts I think I made for the year not in the most popular posts sidebar (on obscurejavascript.tumblr.com as of late December 2019). Since popular doesn't necessarily mean better. The list for 2019 is below:
1. Replaying JavaScript Functions Without Variables (Or Libraries) There are many implications of Functions also being Objects in JavaScript, but one of the most interesting is that you can cache the last value of a function call use a property on itself. This post explains how that is done.
2. What is the Point of Var in Modern JavaScript? Even with let and const now supported in all modern JavaScript environments, there are still some rare use cases where var is necessary.
3. Metaprogramming Classes in JavaScript Compared to many programming languages, even other popular scripting languages, JavaScript tends to be very flexible. Without using any custom eval code - just standard non-verbose syntax, it is possible to create classes from templates. Which can include things like dynamic method names and other variations.
4. String Substitutions In Console Strings One of the little known features in the console object is support for string substitutions. Since most environments display console output of objects in an easy to inspect way (sometimes even with GUI features), this is very useful for debuggging.
5. Quickly Swapping Values Swapping values in objects or even variables is possible now with just one line of code due to destructuring assignments.
Posts of Previous Years
Best Posts Of 2018
Best Posts Of 2017
Best Posts Of 2016
Best Posts Of 2015
14 notes View notes
obscurejavascript 5 years ago
Text
Waiting For the First Returned API Result With Promise.any() in JavaScript
Waiting For the First Returned API Result With Promise.any() in JavaScript
Promise.any resolves with the found value when the first promise successfully resolves. Which is useful for taking the fastest returning result from a set of slow or unstable APIs. For example, the below simulates different potentially slow APIs by using some with long timeouts:
function apiA() { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ some: 'data', from: 'A' }); }, 1000); }); } function apiB() { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ some: 'data', from: 'B' }); }, 200); }); } function apiC() { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ some: 'data', from: 'C' }); }, 1500); }); } async function getData() { console.time('Promise.any completion time'); const data = await Promise.any([ apiA(), apiB(), apiC(), ]); console.info('Retrieved', data); console.timeEnd('Promise.any completion time'); // Retrieved { some: 'data', from: 'B' } // Promise.any completion time: 203.451ms (exact time will vary slightly) } getData();
Without that, some level of management code would be needed. Note that compared to Promise.race which is now supported by major browsers and Node.js, this only resolves by the first successful promise. Promise.race also resolves when any process causes an error.
For my example, I provided a simple Polyfill that does not do error handling since it was only for this example:
if (typeof Promise.any !== 'function') { // Only for example purposes. This does not implement the error handling of Promise.any. For a production ready // implementation instead consider libraries like Bluebird: https://github.com/petkaantonov/bluebird Promise.any = function(promises) { let resolved = false; return new Promise((resolve, reject) => { for (const promise of promises) { promise.then((result) => { if (resolved !== true) { resolved = true; resolve(result); } }); } }); } }
This will be necessary since Promise.any is in stage 3 which means that although the syntax is finalized, it is still awaiting implementation in most JS environments. If you need a production version of Promise.any(), it will be available in many Promise libraries like Bluebird: https://github.com/petkaantonov/bluebird
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2019/promiseAny.js
8 notes View notes
obscurejavascript 5 years ago
Text
Quick Object Searches in Arrays of Objects With KeyBy in JavaScript
keyBy takes an array of object and a property name then creates an object map of that property name to the corresponding array element. This is useful to make checks for various properties in arrays easier and faster. After doing the keyBy direct property tests can be used instead of find loops or other tactics. For example:
const cars = [ { id: 'miata', hp: 155, weight: 2400 }, { id: '4c', hp: 237, weight: 2465 }, { id: 'elise', hp: 217, weight: 2000 } ]; const carIds = keyBy(cars, 'id'); console.log('Cars found (keyBy)'); console.log('miata:', 'miata' in carIds); console.log('elise:', 'elise' in carIds); console.log('ferrari:', 'ferrari' in carIds); // miata: true // elise: true // ferrari: false
The only disadvantage is that the array must be looped through once to create the key-value object:
function keyBy(arr, propertyName) { const obj = {}; for (const ele of arr) { obj[ele[propertyName]] = ele; } return obj; }
keyBy is available in utility libraries like Lodash: https://lodash.com/docs/4.17.15#keyBy
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2019/keyBy.js
3 notes View notes
obscurejavascript 5 years ago
Text
Using ES6 Modules in Node.js
To get this working install Node.js 12:
nvm install 12
Then ensure your files end in .mjs to specify them as modules. Then either run Node.js with --experimental-modules or install the 'esm' package and use -r esm instead. This installation also gets rid of the warning messages that --experimental-modules gives. Here is an example:
backendImports.mjs
import moduleA from './testModule'; moduleA.helloWorld(); // Hello world console.log(moduleA.property1); // value1
testModule.mjs
export const helloWorld = () => { console.log('Hello world'); }; export const property1 = 'value1'; export default { helloWorld, property1 };
Which is run with node -r esm 2019/backendImports.mjs if you installed 'esm'. So far that does exactly what require does, but now at least it matches more modern import syntax used by bundlers like Webpack (which is used by React and modern Angular). Though the real advantage is the flexibility it gives. For example, specific parts of the module can be extracted instead in one line of code:
import { helloWorld } from './testModule'; helloWorld(); // Hello world
All parts of the module can be automatically imported too with * and assigned to a namespace in case no default export was given:
import * as moduleA2 from './testModule'; moduleA2.helloWorld(); // Hello world console.info(moduleA2.property1); // value1
Github Location https://github.com/Jacob-Friesen/obscurejs/blob/master/2019/backendImports.mjs
8 notes View notes