So I understand how Execution Contexts && Activation Object work in ES5, no problem.
I also understand that ES6 is basically syntactic sugar over ES5, so most likely a block-scoped code does also create an Execution Context, just like function scopes.
However, I haven't found any documentation on this matter.
Imagine the following code:
var a = 50
{
a = 51;
let b = 100;
}
The activation objects abstraction would be something like:
globalActivationObject {
scope-chain: Global,
this: this.globalExcutionContext,
var a: 50
}
blockActivationObject {
scope-chain: parentContext(aka Global),
this: globalEC,
a: 51,
let b = 100;
}
Is any of this correct? Is there any good source where I can read more about it?
I have a codeceptjs/puppeteer project and am building a custom helper for accessing information in tables. I have been able to make this work, but only by putting a two second wait in my test step before calling on the async function in my custom helper class. Given that this is all based on async/await, I have to believe I am just missing something and there is a clean way to do this.
Function from my helper class.
async getRowCount() {
//const browser = this.helpers['Puppeteer'].browser;
const page = this.helpers['Puppeteer'].page;
page.waitForSelector('tbody');
let rowCount = await page.$$eval('tbody tr', rows => rows.length);
return rowCount;
// These work
// page.waitForSelector('a[href="#/site/create"]');
// page.click('a[href="#/site/create"]');
}
My codeceptjs scenario is below.
Scenario.only('Table check ALL', async (I, loginAs) => {
loginAs('bob');
I.say(await I.getRowCount());
I.wait(3);
});
When the code is as shown above, my row count that is returned in always 0.
However, if I put a 1 second wait just before the I.getRowCount() function, then the correct total number of rows for the tbody tr selector is returned.
If anyone can help me understand why this is happening and what I can do to fix it so I don't have to pepper my code with manual wait steps to accommodate these helper functions (core "feature" of codeceptjs), I would greatly appreciate it.
Thank you!
You need to await waitForSelector:
await page.waitForSelector('tbody');
Almost all page methods are returning promises, so you have to await them.
Is there some elegant way how to get an element handle from nodeId? I get a list of all nodes (and nodeIds) by
const nodes = await page._client.send("DOM.querySelectorAll", {
nodeId: doc.root.nodeId,
selector: "*"
});
where nodes.nodeIds is a list of integers. And somehow I would like to run
const html = await page.$eval(nodeId, node => node.offsetParent === null);
To check if the element is visible. The problem is that page.$eval expects string (selector) as a first argument.
I could add a custom attribute through DOM.setAttributeValue and then query that through page.$eval but that seems hacky. So is there some more direct way how to go from devtool-protocol nodeIds to Puppeteer's ElementHandle?
So there is a way how to go from Puppeteer's ElementHandle to nodeId.
const element = page.$('a');
const node = await page._client.send("DOM.requestNode", {
objectId: element._remoteObject.objectId
});
// <- { nodeId: 1 }
My original question asks for the other direction (nodeId -> ElementHandle) but I can work with this. It's also more practical to use Puppeteer by default whenever possible.
I thought ES6 module exports were always immutable, so I'm pretty confused about the behaviour I'm getting. I have a simple array of colors that I would like to use in multiple components of my Vue application. It is in it's own file like so:
export const colors = [
'#ffb3ba',
'#ffdfba',
'#ffffba',
'#bae1ff',
]
Then I import it into the component where I want to use it like this:
import { colors } from '../assets/colors';
I have a function for picking a random color and then removing it from the list so it isn't picked again for the same component. It's something like this.
descriptions.forEach(item => {
const colorIndex = Math.floor(Math.random() * colors.length);
item['color'] = colors[colorIndex];
colors.splice(colorIndex, 1);
});
The idea here is to pick a random color from the list, assign it a description and then remove it from the list so a different one is picked on the next iteration of the forEach.
The problem is that it seems to be removing the colors from the list permanently. So when I import and try to use the array in another component, there are no colors in it. How can I make it so there is a fresh instance of the colors array for every component?
The imported bindings are not assignable, that's all. They are similar to const - you cannot change the variable, but you can mutate the object it holds. To prevent that, freeze the object when exporting it:
export const colors = Object.freeze([
'#ffb3ba',
'#ffdfba',
'#ffffba',
'#bae1ff',
]);
How can I make it so there is a fresh instance of the colors array for every component?
Have a look at Copying array by value in JavaScript for that: just colors.slice(). Also you'll want to check out How to randomize (shuffle) a JavaScript array? for how to efficiently get the random colors for your descriptions - there are even some answers that do not mutate the input.
import { colors } from '../assets/colors';
import { shuffle } from '…';
const randomColors = shuffle(colors.slice());
console.assert(descriptions.length <= randomColors.length);
for (const [i, description] of descriptions.entries())
description.color = randomColors[i];
ES6 module imports are not immutable, as you have correctly observed.
You could create a shallow copy of the array and operate on that one:
const copiedColors = [...colors];
descriptions.forEach(item => {
const colorIndex = Math.floor(Math.random() * colors.length);
item['color'] = copiedColors[colorIndex];
copiedColors.splice(colorIndex, 1);
});
I have a JSON file which I need to iterate over, as shown below...
{
"device_id": "8020",
"data": [{
"Timestamp": "04-29-11 05:22:39 pm",
"Start_Value": 0.02,
"Abstract": 18.60,
"Editor": 65.20
}, {
"Timestamp": "04-29-11 04:22:39 pm",
"End_Value": 22.22,
"Text": 8.65,
"Common": 1.10,
"Editable": "true",
"Insert": 6.0
}]
}
The keys in data will not always be the same (i've just used examples, there are 20 different keys), and as such, I cannot set up my script to statically reference them to get the values.
Otherwise I could state
var value1 = json.data.Timestamp;
var value2 = json.data.Start_Value;
var value3 = json.data.Abstract;
etc
In the past i've used a simple foreach loop on the data node...
foreach ($json->data as $key => $val) {
switch($key) {
case 'Timestamp':
//do this;
case: 'Start_Value':
//do this
}
}
But don't want to block the script. Any ideas?
You can iterate through JavaScript objects this way:
for(var attributename in myobject){
console.log(attributename+": "+myobject[attributename]);
}
myobject could be your json.data
I would recommend taking advantage of the fact that nodeJS will always be ES5. Remember this isn't the browser folks you can depend on the language's implementation on being stable. That said I would recommend against ever using a for-in loop in nodeJS, unless you really want to do deep recursion up the prototype chain. For simple, traditional looping I would recommend making good use of Object.keys method, in ES5. If you view the following JSPerf test, especially if you use Chrome (since it has the same engine as nodeJS), you will get a rough idea of how much more performant using this method is than using a for-in loop (roughly 10 times faster). Here's a sample of the code:
var keys = Object.keys( obj );
for( var i = 0,length = keys.length; i < length; i++ ) {
obj[ keys[ i ] ];
}
You may also want to use hasOwnProperty in the loop.
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
switch (prop) {
// obj[prop] has the value
}
}
}
node.js is single-threaded which means your script will block whether you want it or not. Remember that V8 (Google's Javascript engine that node.js uses) compiles Javascript into machine code which means that most basic operations are really fast and looping through an object with 100 keys would probably take a couple of nanoseconds?
However, if you do a lot more inside the loop and you don't want it to block right now, you could do something like this
switch (prop) {
case 'Timestamp':
setTimeout(function() { ... }, 5);
break;
case 'Start_Value':
setTimeout(function() { ... }, 10);
break;
}
If your loop is doing some very CPU intensive work, you will need to spawn a child process to do that work or use web workers.
If you want to avoid blocking, which is only necessary for very large loops, then wrap the contents of your loop in a function called like this: process.nextTick(function(){<contents of loop>}), which will defer execution until the next tick, giving an opportunity for pending calls from other asynchronous functions to be processed.
My most preferred way is,
var objectKeysArray = Object.keys(yourJsonObj)
objectKeysArray.forEach(function(objKey) {
var objValue = yourJsonObj[objKey]
})
If we are using nodeJS, we should definitely take advantage of different libraries it provides. Inbuilt functions like each(), map(), reduce() and many more from underscoreJS reduces our efforts. Here's a sample
var _=require("underscore");
var fs=require("fs");
var jsonObject=JSON.parse(fs.readFileSync('YourJson.json', 'utf8'));
_.map( jsonObject, function(content) {
_.map(content,function(data){
if(data.Timestamp)
console.log(data.Timestamp)
})
})
A little late but I believe some further clarification is given below.
You can iterate through a JSON array with a simple loop as well, like:
for(var i = 0; i < jsonArray.length; i++)
{
console.log(jsonArray[i].attributename);
}
If you have a JSON object and you want to loop through all of its inner objects, then you first need to get all the keys in an array and loop through the keys to retrieve objects using the key names, like:
var keys = Object.keys(jsonObject);
for(var i = 0; i < keys.length; i++)
{
var key = keys[i];
console.log(jsonObject.key.attributename);
}
Not sure if it helps, but it looks like there might be a library for async iteration in node hosted here:https://github.com/caolan/async
Async is a utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript. Although originally designed for use with node.js, it can also be used directly in the browser.
Async provides around 20 functions that include the usual 'functional' suspects (map, reduce, filter, forEach…) as well as some common patterns for asynchronous control flow (parallel, series, waterfall…). All these functions assume you follow the node.js convention of providing a single callback as the last argument of your async function.
Take a look at Traverse. It will recursively walk an object tree for you and at every node you have a number of different objects you can access - key of current node, value of current node, parent of current node, full key path of current node, etc. https://github.com/substack/js-traverse. I've used it to good effect on objects that I wanted to scrub circular references to and when I need to do a deep clone while transforming various data bits. Here's some code pulled form their samples to give you a flavor of what it can do.
var id = 54;
var callbacks = {};
var obj = { moo : function () {}, foo : [2,3,4, function () {}] };
var scrubbed = traverse(obj).map(function (x) {
if (typeof x === 'function') {
callbacks[id] = { id : id, f : x, path : this.path };
this.update('[Function]');
id++;
}
});
console.dir(scrubbed);
console.dir(callbacks);