I have a code block like the below, it's a sync function.
I want to collect in reply info on items.
However reply is always returning as the empty array even when I have items.
When I check in the debugger, the info shows reply as a closure variable, not a local.
I'm wondering if there's something going on with hoisting here that I don't understand?
invStatus() {
let reply: string[] = []
Logger.log('player.status.items:', this.items)
if (!this.items.length) {
reply.push('nothing')
} else this.items.map(item => {
Logger.log('item', item)
reply.push[`- ${item.name}`]
})
Logger.log('player.status.reply:', reply)
return reply
}
hmm this is typescript also, I wonder if the transpiler behavior is subtly different?
I probably should use a .forEach here since I'm not capturing the return of the map or transforming things but I understand that has the same iterator behavior. The only other thing would be to go with a for x of which is more reliable, but I'd like to understand the issue here!
I think you have made a syntax error.
Observe this line:
reply.push[`- ${item.name}`]
Here, instead of parantheses, you have used square brackets.
Corrected code would be,
reply.push(`- ${item.name}`)
I know javascript but not typescript but I believe this must be the cause of the issue.
Related
I've run into the strange assertion in a book in my point of view. I guess that I don't understand something but anyway it will be great if you shed light on the situation.
ajax('<host1>/items',
items => {
for (let item of items) {
ajax(`<host2>/items/${item.getId()}/info`,
dataInfo => {
ajax(`<host3>/files/${dataInfo.files}`,
processFiles);
});
}
});
An author pay attention on :
There’s another hidden problem with this code. Can you guess what it is? It occurs when you mix a synchronous artifact like a for..of imperative block invoking asynchronous functions. Loops aren’t aware that there’s latency in those calls, so they’ll always march ahead no matter what, which can cause some really unpredictable and hard-to-diagnose bugs. In these situations, you can improve matters by creating closures around your asynchronous functions, managed by using forEach() instead of the loop.
Instead of it they offer the following:
ajax('/data',
items => {
items.forEach(item => {
// process each item
});
});
Frankly speaking I expected that if we use let for loop it means we create a closure for each iteration therefore I don't see any hidden problems there.
You are correct, if the author's comment is on that exact code snippet, they were mistaken.
Loops aren’t aware that there’s latency in those calls [...] you can improve matters by [...] using forEach()
That changes nothing, forEach() is equally unaware of async calls made inside its callback as a for loop is of async calls made in its body. forEach() will "always march ahead" the same way a for loop will.
With let you cannot encounter the issue that the author seems to be worried about, as each iteration of the loop has its own item just like when using items.forEach( item => { ... .
Even with var there is no issue with that code, since the variable item is not used inside the callback to the ajax request. You could produce the author's concern by using var and using item inside the callback, such as: console.log( item.getId() );.
Note: It is important to be aware that the callbacks will most likely run in a different (seemingly random) order than they were initiated in. If you aren't aware of that it can cause surprising bugs, but that also has nothing to do with using a loop vs. forEach.
The authors of that book seem to have no clue. There is no problem of for (let … of …) that .forEach(…) would fix.
They talk about
creating closures around your asynchronous functions, managed by using forEach() instead of the loop
but the closure is not created by the forEach callback function, the closure is the callback passed into the ajax function. It closes over the surrounding scope, and there is hardly any difference between the for block scope (when using let or const) and the function body scope (when using forEach).
Why
I'm a sucker for compact code and new language features. Therefore, I'm trying to find a way to reuse the value of the condition statement which opened the current scope without saving this beforehand as a variable in the parent scope, where it has no further use.
This would make my code more compact, yet easier to read, which is a pretty subjective feat. There probably are multiple of approaches to this, so all kinds of solutions are welcome (preferrably ES8, this is not for production use/professional environments).
Clarification:
The following function is cpu-intensive
fetchData() // returns [object Object] or false on error
It can also be an input stream whose output will differ upon every function call, or a key-value pair lookup which has to happen thousands of times in a short period but can only happen synchronously/blocking.
My current approach:
I have to type data 3 times and it pollutes the global scope. Also, while debugging with breakpoints, the variable is not found in the smallest possible scope.
let data = fetchData()
if (data)
useData(data)
else
error()
Not a good solution:
The data variable still occurs 3 times, pollutes the parent scope and you don't know what it means yet upon declaration. Single equation (=) sign can be interpreted wrongly by the reader OR corrected by someone else by mistake.
let data
if (data = fetchData())
useData(data)
The lazy way:
if (fetchData())
use(fetchData())
What i wish for:
The 'let' avoids interpreting it as an == conditional statement:
if (let data = fetchData()) { // data is declared in the child scope
parseData(data)
} else {
console.error('meh')
}
Result: SyntaxError: Unexpected identifier
or
if (fetchData()) {
parseData(fi)
}
or
// some kind of symbol, i know this is xor, but it seemed appropriate
if (long.object.key.value().chain.data)
return JSON.parse(^)
or
if (fetchData()) {
parseData(condition.value) // or .pop() or just 'condition'
}
I just feel like it's an information overload when looking back at my current approach, even if it has its advantages. I believe this can improve readability in situations where code is really dense or where function/variable names are so obvious they almost form a sentence.
Here is a way to do it without any declared variable:
[fetchData()].filter(Boolean).map(useData);
This is just for fun, I would not advise to actually use it, as it is neither efficient, nor very clear code.
NB: If you also need the meh output... then extend to:
[fetchData()].filter(Boolean).map(useData).length || console.log('meh');
No, there is no such syntax feature. The way to go is either a block scope
{
const data = fetchData()
if (data)
useData(data)
else
error()
}
or an IIFE
(function(data) {
if (data)
useData(data)
else
error()
}(fetchData()));
which you can of course also make into a reusable function:
function If(predicate, then, other) {
return function(value) {
if (predicate) then(value) else other(value)
};
}
If(Boolean, useData, error)(fetchData());
I come from a compiled-language background (C/C++/Objective-C) and am currently writing a full-fledged application in JavaScript. (TypeScript actually, but my question is the same for both.)
The problem I'm running into is that if a single error occurs, the entire flow of execution halts. For instance, if I have:
myFunction()
{
doSomethingA();
doSomethingB();
doSomethingC();
}
then if doSomethingA() has something like this:
var myValue = window.myData.myValue;
but "myData" doesn't exist on "window" at the time, then all code STOPS EXECUTING... doSomethingB() and doSomethingC() do not execute, and a console error is logged. That might be fine for simple web pages, but I'm creating an application, and it needs to not 'stop working' inexplicably.
Granted, I can use try/catch to be 'aware' of this error, but that STILL doesn't solve my problem: I would like to write the code in a way such that doSomethingB() and doSomethingC() continue to execute, even if a problem arises.
However, this is a huge over-simplification of my code. Imagine there are MANY of these functions. It would be impractical to surround each with its own separate try/catch block. Even if I did, I need the rest of a given function to continue to execute even if something in the first part fails.
Of course, I can 'protect' my variables by using:
if ( typeof window.myData != "undefined")
or
if (window.myData.hasOwnProperty("myValue")
but that becomes very messy if you have several levels to check, such as when accessing:
var myValue = window.myData.something.anotherLevel.somethingElse.value;
Now I have to check if myData, something, anotherLevel, and somethingElse are all valid before accessing this value 'safely'. This results in very ugly code.
Is there a better way to make my code more 'bullet-proof'? If a property is unexpectedly missing, I need code to continue executing after the problem statement. Is that possible without 'protecting' every single statement that accesses data that has even a tiny chance of being undefined?
That's just how JavaScript is. It's not a strongly-typed language. You have the right approach checking for falsy/undefined. You could look into a utility library, something like Lodash's isUndefined() method to help ease the pain a bit. They have a lot of helper methods like that such as checking for object types, arrays, object literals, etc.
https://lodash.com/docs#isUndefined
Normally You have to have some set of "nested if hells" like:
if(typeof window.myData !== 'undefined' && typeof window.myData.something !== 'undefined' && typeof window.myData.something.anotherlevel !== 'undefined' ...) {
}
To ease developers pain there is utilities for example lodash
it's very helpful and shortens Your code.
it has _.get(object, path) function that takes element by path and if not found it just returns undefined.
<script src="https://raw.githubusercontent.com/lodash/lodash/4.13.1/dist/lodash.js"></script>
<script>
window.myData = {
something: {
anotherlevel: {
value: 'YOU GOT ME! (:'
},
zerolevel: {
value: 0
}
}
};
function getDataByPath(path) {
var result = _.get(window, path);
alert(result);
}
</script>
<button onclick="getDataByPath('myData.something.anotherlevel.value')">
anotherlevel.value
</button>
<br/><br/>
<button onclick="getDataByPath('myData.something.zerolevel.value')">
zerolevel.value
</button>
<br/><br/>
<button onclick="getDataByPath('myData.something.unexistentlevel.value')">
unexistentlevel.value
</button>
Your best bet would be to check, if the variables you use are undefined or not with
typeof window.yourVariable === "undefined"
It's however not very safe to rely on variables in the window object, use scopes and closures.
I'm accepting Prefix's answer, since I don't like accepting my own answers. And, it's useful to hear about the Lodash helper, and know that the "nested if hells" I have are a normal (sad to hear it!) part of JavaScript.
However, the 'solution' I've gone with that has ended up making me feel very happy, is to actually reject a premise in my original question: that try/catch is not a good solution.
Turns out try/catch, combined with attempting to make sure variables are undefined, is a good way to catch those cases that I've missed.
I've actually ended up architecting it as follows. Let's say I have lots of functions I want to happen as a result of doStuff(), and I've put them into window.callbacks. I can then do (untested pseudocode, but it gives you the gist of it):
doStuff() {
for (var myFunc in window.callbacks) {
if (window.callbacks.hasOwnProperty(myFunc) ) {
try {
window.callbacks[myFunc].call();
}
catch(err) {
console.log("%cCaught error in " + myFunc + ": " + (err.stack || err), 'background: #222; color: #bada55');
}
}
}
}
This logs a unique console log message, complete with stack trace, showing what triggered the error, and yet CODE EXECUTION CONTINUES. Subsequent callbacks will still be called. The only code that doesn't execute is the remainder of the callback that caused the error.
Given my compiled-language background, this gives me warm fuzzy feelings... it means that at worst, a tiny function will fail to execute, not an entire code path following an error.
I'm learning Javascript and I wrote the following code:
if (mystring.len > 0) {
// do stuff
}
I accidentally used .len instead of .length. To my surprise, no error was raised. mystring.len returned undefined and this made the comparison fail but the code kept right on running. I would prefer an actual error to be raised so I can fix the code. Adding "use strict" didn't help, nor did jslint.
I know there are ways to actively check whether or not a property exists, but that's not what I'm looking for. I want Javascript to tell me when I've made a typo in a property name.
Is there a way to cause Javascript to give an error in this case?
Nope - that is how JavaScript works and it's incredibly useful. Who is to say that checking len is something that needs fixing? Consider:
if(mystring.len === undefined) {
mystring.len = "Test";
}
The best you can do is to simply check that the thing is defined before using it:
if(mystring.len !== undefined) {
}
I appreciate the strangeness, and how it doesn't feel robust (having originally come from a C# background) but there isn't a lot you can do unfortunately. The fact that JavaScript is case sensitive makes this even more frustrating. You will learn to live with it though!
If you really really wanted to run some static analysis then you could considering creating a transpiler (e.g. Babel) extension to run this sort of analysis - but it would get really difficult if you ever expected something to be undefined which I find is common place.
edit
Here's a real example that I'd use where undefined is useful. I'm working with a library that needs to move stuff from one location to another. It can't do that unless the original location has been specified, so I might write something like the following, initializing values if I don't have them for some reason:
function update(node) {
if(node.x === undefined) { node.x = 0; }
node.y = node.y || 0; // This is the shorthand way I'd actually write it
// Do some other stuff
};
"use strict" (in my experience) is used so that variables that aren't explicitly declared/instantiated that are then referenced will throw errors (else, JS would just make a new var on the fly). So that wouldn't help here.
This sounds like an error that would typically be picked up by a compiler in other languages, but since JS is interpreted, you won't have that kind of explicit error checking unless you're in a beefy IDE. Are you using a text editor or something to write JS?
Thats not the way JavaScript considers your above code. Every variable in JS is an object. So, when you do mystring.len, its actually trying to access the len property of mystring obj and when it doesn't find that property, it will return undefined - which is how it should be. Thats why you will not be able to find any error using JSLint.
Just to give you an example -
var myObj = {name: 'Hello', id: 1};
console.log(myObj.name); // Returns 'Hello'
console.log(myObj.text); // 'undefined'
In order to prevent such code from giving you any errors, you can easily use the hasOwnProperty() method like follows-
if(myObj.hasOwnProperty('text')) doSomething();
Since myObj doesn't have any property text, the doSomething() function will never be called.
This is the behaviour of JavaScript as mentioned by many/all answers. However there is an option to prevent new properties you might want to try:
Object.seal https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal
The simple answer is JavaScript is not ment to be typesafe...You shouldn't check it, but if you still want to check you can do it by:
if ('len' in mystring){
}
You should look into Typescript if you ask this question...
Until today, I had not known the with operator existed. I stumbled upon it while debugging an issue being thrown from a plugin (Backbone.Epoxy).
The operator creates block level scope for each property on the passed object.
var testObj = { "cat":true };
with (testObj) {
console.log(cat ? "Cat!": "dog"); // Cat!
}
Simple enough? Initially I thought this could potentially be really cool. Until I realized why my code was throwing an error. Here is an example derived from my code.
var testObj = { "css":true, "background-color":"blue" };
with (testObj) {
console.log(css ? background-color : ""); // throws
}
The actual code is a bit more dynamic, but this is essentially what occurs behind the scenes in the plugin. Since dashes are not allowed within variable names but are allowed in property names, which cause the error to be thrown.
So, to the questions:
Is there a way to sanitize the block scope local variable in order to avoid the issues with the dash while keeping it in my property name?
Has anyone else worked around this issue with epoxy?
You would have to make an exception and write:
testObj["background-color"]
As you may suspect, you cannot write just background-color, for the same reason you cannot write testObj.background-color. You should also ask whether using with, which is fairly non-standard, is worth the character-count savings. Usually the answer is "no".