Good day all.
Today i'm working in this complex script that makes request's to a site with server-side rendering, get's the HTML, breaks and grabs some data. The script has 4 phases: phaseOne, phaseTwo, phaseThree and phaseFour.
Which phases has a similar interface:
class PhaseOne {
constructor(MAP) {
this.MAP = MAP || MAP;
}
// All code related with the phase here.
process() {}
}
So i'm working upon this MAP object in all phases, and i'm calling each phase in a stack, like this:
let phases = require('./phases');
[
// 'Initial',
'PhaseOne',
'PhaseTwo',
'PhaseThree',
'PhaseFour',
].reduce((m, fn) => {
return new phases[fn](m).process();
}, MAP);
Everything is working fine. My problem is: Some phases are REALLY slow.. all the program will take 30 minutes to finish.. and i would like to see in my terminal the percentage of each phase.
Like:
PhaseOne: 10%
PhaseOne: 11%
PhaseOne: 12%
But i don't have any idea and i can't find a good tutorial to do that..
Currently inside my process functions i have for loops, if statements.. in general i'm using imperative style..
An example of PhaseOne:
// In this phase we need the property string in MAP.aguia01 to
// assign the first context and extract the data with regex.
if (typeof this.MAP.aguia01.string === 'undefined') {
cli.fatal(
'MAP doesn\'t have the necessary properties to run in Aguia01 phase. Exiting...'
);
}
for (let month of months) {
this.MAP.aguia01.string += requests.aguia01.apply(this, [month]);
}
for (let info of patterns.aguia01.infos) {
this.MAP.aguia01.infos[info.name] = (
this.MAP.aguia01.string.match(info.pattern)
)[1];
}
for (let line of patterns.aguia01.lines) {
this.MAP.aguia01.lines[line.name] = (
this.MAP.aguia01.string.match(line.pattern)
)[1];
}
So.. Is it possible to do what i want with imperative style?
Thanks.
There is the progress package but it's only up to you how you define "progress". You define a number of ticks corresponding to the completed state and then, you just call a method on the progress bar to make it "progress". An example:
var ProgressBar = require('progress');
// 10 ticks to complete the task
var bar = new ProgressBar(':bar', { total: 10 });
var timer = setInterval(function () {
// make the bar tick(), each call will make a 10% progress
bar.tick();
if (bar.complete) {
console.log('\ncomplete\n');
clearInterval(timer);
}
}, 100);
How about keeping a context object for progress outside of your reduce call? You could make it an event emitter, and then pass it in to your process function. Inside your process function you could emit progress events, which could then be logged. Perhaps something like this:
let phases = require('./phases');
//set up
let progressMonitor = new require('events')
progressMonitor.on('progress', percentDone => {
console.log(percentDone + '% done')
})
// your existing code
[
// 'Initial',
'PhaseOne',
'PhaseTwo',
'PhaseThree',
'PhaseFour',
].reduce((m, fn) => {
return new phases[fn](m).process(progressMonitor);
}, MAP);
and then inside your process functions:
class PhaseOne {
constructor(MAP) {
this.MAP = MAP || MAP;
}
// All code related with the phase here.
process(progressMonitor) {
//do work
progressMonitor.emit('progress', 10)
//more work
progressMonitor.emit('progress', 15)
//much work
progressMonitor.emit('progress', 75)
}
}
Related
I'm listening to the observable that may return true or false value - the only thing that I want to do is to set throttleTime for function call when it's true and don't have it when it's false. So I did some kind of workaround for that but I don't like this solution. I have tried a different approach where I tried to do it in the actions' effect but without success..
So this is the observable:
this.store$
.pipe(
takeUntil(this.componentDestroyed$),
select(selectGlobalsFiltered([
GlobalPreferencesKeys.liveAircraftMovement])),
distinctUntilChanged()
)
.subscribe((globals) => {
if (globals && globals[GlobalPreferencesKeys.liveAircraftMovement] !== undefined) {
this.isLiveMovementEnabled = (globals[GlobalPreferencesKeys.liveAircraftMovement] === 'true');
}
if (!this.isLiveMovementEnabled) {
this.processPlaneData = throttle(this.processPlaneData, 4000);
} else {
this.processPlaneData = this.notThrottledFunction;
}
});
And as you can see I've created excat the same method that is 'pure' - notThrottledFunction and I'm assigning it when it's needed.
processPlaneData(data: Airplane[]): void {
this.store$.dispatch(addAllAirplanes({ airplanes: data }));
}
notThrottledFunction(data: Airplane[]): void {
this.store$.dispatch(addAllAirplanes({ airplanes: data }));
}
So basically this is working solution, but I'm pretty sure there is a better approach for doing such a things.
*throttle(this.processPlaneData, isLiveMovementEnabled ? 0 : 4000) doesn't work
So the second approch where I tried to do this inside of effect, I added a new argument for addAllAirplanes action - isLiveMovementEnabled: this.isLiveMovementEnabled
addAllAirplanes$ = createEffect(() =>
this.actions$.pipe(
ofType(ActionTypes.ADD_ALL_AIRPLANES),
map((data) => {
if (data.isLiveMovementEnabled) {
return addAllAirplanesSuccessWithThrottle(data);
} else {
return addAllAirplanesSuccess(data);
}
}
)
);
And then I added another effect for addAllAirplanesSuccessWithThrottle
addAllAirplanesThrottle$ = createEffect(() =>
this.actions$.pipe(
ofType(ActionTypes.ADD_ALL_AIRPLANES_THROTTLE),
throttleTime(4000),
map((data) => addAllAirplanesSuccess(data))
)
);
But it doesn't work. Can someone help me?
(It's not clear how the data arrives in your example code, but I'll assume an Observable)
throttle and throttleTime are similar to your use case, but I think it makes sense to build your own custom implementation without them. I'd suggest managing the timing yourself, using Date to determine time deltas.
You've already cached the live filtering boolean in the component state, so we can just handle all of the data stream from your position update observable and filter them out manually (which is all throttle does, but it expects you to be able to feed it an interval at subscription time, and yours needs to be dynamic).
Setup component scoped variables to contain previous timestamps, as
private prevTime: number;
private intervalLimit: number = 4000;
Supposing data$ is your input plane position data stream:
data$.pipe(filter(data => {
const now: number = Date.now();
const diff = now - this.prevTime;
if (this.isLiveMovementEnabled) {
// no throttle - pass every update, but prepare for disabling too
// record when we last allowed an update & allow the update
this.prevTime = now;
return true;
} else if (diff > intervalLimit) {
// we are throttling results, but this one gets through!
this.prevTime = now;
return true;
} else {
// we're throttling, and we're in the throttle period. eat the result!
return false;
}
}
Something like that gives you full control over the logic used whenever data comes in. You can add other operations like takeUntil and distinctUntilChanges into the pipe and trust that when you subscribe you'll be getting updated when you want them.
You can even adjust the intervalLimit to dynamically adjust the throttle period on the fly.
I am trying to set volume over 1 on an audio element, following this article.
https://cwestblog.com/2017/08/17/html5-getting-more-volume-from-the-web-audio-api/
I need to be able to set it more than once, so i've set a global array to store the different results in so that I can adjust based on participants.
This does not seem to be working though, to me I can't tell any difference when the gain is set, am I doing something wrong?
window.audioGainParticipants = [];
function amplifyMedia(participant_id, mediaElem, multiplier) {
const exists = window.audioGainParticipants.find(
(x) => x.participant_id === participant_id
);
let result = null;
if (exists) {
result = exists.result;
} else {
var context = new (window.AudioContext || window.webkitAudioContext)();
result = {
context: context,
source: context.createMediaElementSource(mediaElem),
gain: context.createGain(),
media: mediaElem,
amplify: function (multiplier) {
result.gain.gain.value = multiplier;
},
getAmpLevel: function () {
return result.gain.gain.value;
},
};
result.source.connect(result.gain);
result.gain.connect(context.destination);
window.audioGainParticipants.push({ participant_id, result });
}
result.amplify(multiplier);
}
I call this like this...
const audioElement = document.getElementById(
`audio-${participantId}`
);
amplifyMedia(
`audio-${participantId}`,
audioElement,
volume // number between 0-2
);
That article might be outdated. You're not supposed to directly assign to the gain's value, but instead use a setter method.
setValueAtTime is pretty simple, and should meet your needs. The docs show an example of calling this method on a gain node.
https://developer.mozilla.org/en-US/docs/Web/API/AudioParam/setValueAtTime
There's also setTargetAtTime which is a tiny bit more complex, but should sound better if you need to change settings on something that is currently playing.
https://developer.mozilla.org/en-US/docs/Web/API/AudioParam/setTargetAtTime
I am trying to use RxJS to poll for events. However, I only have access to one function, which is getEvent(). I can do 2 things with the getEvent function:
getEvent("latest") — this will give me the latest event object
getEvent(eventId) - I pass in an integer and it will give me the event object corresponding to the eventId.
Event IDs always increment from 0, but the problem is, if my polling interval isn't small enough, I might miss events.
For example, if I do a getEvent("latest") and I get an event that has an ID of 1, that's great. But if the next time I call it, I get an ID of 3, I know that I missed an event.
In this case, I want to use a higher-order observable to call getEvent(2) and getEvent(3) so that the consumer of the stream I am creating won't have to worry about missing an event.
Right now, all I have is something like this:
timer(0, 500).pipe(
concatMap(() => from(getEvent("latest"))
)
For some context, I'm working off of this blogpost: https://itnext.io/polling-using-rxjs-b56cd3531815
Using expand to recursively call GET fits here perfectly. Here is an example with DEMO:
const source = timer(0, 2000)
const _stream = new Subject();
const stream = _stream.asObservable();
const s1 = source.pipe(tap(random)).subscribe()
const sub = stream.pipe(
startWith(0),
pairwise(),
concatMap((v: Array<number>) => {
let missing = v[1] - v[0];
return missing ? getMissing(v[0], missing) : EMPTY
})
).subscribe(console.log)
function getMissing(start, count) {
return getById(start).pipe(
expand(id => getById(id+1)),
take(count)
)
}
// helper functions for DEMO
let i = 1;
function random() {. // THIS IS YOUR getEvent('latest')
if (i < 10) {
i+=2;
_stream.next(i
// (Math.floor(Math.random() * 8))
)
}
}
function getById(id) {. // THIS IS YOUR getEvent(eventId)
return of(id).pipe(delay(1000)) // delay to mimic network
}
I've become hopelessly addicted to Screeps recently, and I refactored some code to make a task-based implementation. Tasks are things like "walk to and then harvest until you are at full capacity" and are based off of a single base task template written as an ES6-style class. Creeps can be assigned tasks through a wrapper (tasks.js) that loads the relevant task file and returns a new task instance.
Today I ran into a strange bug that makes me think I don't fully understand Javascript's inheritance model. Below is the relevant code:
Task.js: (base task class)
class Task {
constructor(taskName) {
// Parameters for the task
this.name = taskName; // name of task
this.quiet = false; // suppress console logging if true
this.creep = [creep assigned to this task]
this.target = [thing task operates on, e.g. "repair this wall"]
...
}
...
// Execute this task each tick. Returns nothing unless work is done.
step() {
...
if (creep.pos.inRangeTo(target, this.targetRange)) {
var workResult = this.work();
console.log(this.quiet) // < returns false, should be true?
if (workResult != OK && this.quiet == false) {
creep.log("Error: " + workResult); // < is printed when run
}
return workResult;
} [else move to target]
}
...
// Task to perform when at the target
work() {
// overwrite this in child class
}
}
module.exports = Task;
task_harvest.js: (harvesting task)
var Task = require('Task');
class taskHarvest extends Task {
constructor() {
super('harvest');
// no mention of this.quiet here
}
...
work() {
console.log("harvest:" + this.quiet);
return this.creep.harvest(this.target);
}
}
module.exports = taskHarvest;
tasks.js: (wrapper to generate a new task instance via a function call)
module.exports = function (taskName) {
var TaskClass = require('task_' + taskName); // all tasks follow this naming pattern
var taskInstance = new TaskClass;
return taskInstance;
};
harvester.js: (behavior model for a harvester creep)
var tasks = require('tasks');
var roleHarvester = {
...
harvest: function (creep) {
var target = Game.getObjectById(creep.memory.assignment);
var taskHarvest = tasks('harvest');
taskHarvest.quiet = true; // < this task shouldn't print anything
creep.assign(taskHarvest, target); // assigns to creep.task
return OK;
},
...
run: function (creep) { // executed every tick
// execute the task
creep.task.step();
},
...
}
When I assign a creep to harvest from a source, I create a new task from task_harvest.js, set its quiet property to be true, and bind it and its target to the creep. Once the creep has a task it is instructed to run it until it becomes invalid (code not included above). The creep executes the task fine, but it still logs everything to the console.
I would think that in harvester.js, when I set taskHarvest.quiet = true;, the behavior imported from Task.js would see this.quiet as true. However, it seems as though that is not the case. In roleHarvester, running console.log(creep.task.quiet) returns true, but in Task, running console.log(this.quiet) when the creep is executing the assigned task gives false.
I could add quiet into the constructor as an optional parameter, but that's convoluted and I want to know why what I'm doing isn't working.
Nevermind, it actually wasn't an inheritance problem; it was a problem caused by the game mechanics: taskHarvest.quiet wasn't getting deleted each tick. Screeps only allows you to store JSON-serializable objects in memory, so I store the task settings in memory and reconstruct the task object each tick:
Object.defineProperty(Creep.prototype, 'task', {
get: function () { // provide new task object recreated from literals stored in creep.memory.task
if (this.memory.task != null) {
var task = tasks(this.memory.task.name);
task.creepName = this.memory.task.creepName;
task.targetID = this.memory.task.targetID;
task.data = this.memory.task.data; // < task.quiet is now task.data.quiet
return task;
} else {
return null;
}
},
set: function(newTask) {
if (newTask != null) {
this.log("use Creep.assign() to assign tasks. Creep.task = ___ should only be used to null a task.");
} else {
this.memory.task = newTask;
}
}
});
taskHarvest.quiet wasn't getting stored in memory, so it wouldn't persist past the first tick of the task. I now store all instance-level adjustable parameters in a task.data object, so task.quiet is now task.data.quiet. This fixed the problem; sorry for producing any confusion!
I have a function that hides and shows items on my page based on what a factory provides me:
function toggleMenuItems(config) {
// hide all our elements first
$(".js-quickMenuElement").hide();
config.data = config.data || [];
config.data.forEach(function (d) {
if (d === config.viewConfigCatalog.CalendarLink) {
$("#CalendarLink.js-quickMenuElement").show();
}
if (d === config.viewConfigCatalog.ProductCreation) {
$("#ProductCreation.js-quickMenuElement").show();
}
// etc etc etc
});
};
We've been using Jasmine for our javascript unit tests and we're discussing whether we should test this function.
Some say that we don't need to because testing this is coupling the view to the javascript test, but at the same time, if instead of jquery .show and .hide functions those were wrappers, or other functions we would test them.
Following on this what would be the best way to test this?
Making a wrapper function that takes in a string and injects the string name in the jQuery select seems wrong.
Another option we thought of is spying on ($.fn, "show") but that would only let us test if show was called X amount of time and not what was hidden...
Thanks,
You can use jQuery to test the visibility of an element.
$(element).is(":visible");
code taken from a related question
Of course in doing this as you say you're coupling the view with the test. You could move the logic which determines the outcome of this function into a separate function and then test that functions result instead.
** Edit **
Below illustrates what I meant regarding simplification with a KVP list, and you could write a test for the function which gets the value from the KVP.
var config = {
data: [],
viewConfigCatalog: {
CalendarLink: "CalendarLink",
ProductCreation: "ProductCreation",
}
};
var kvp = [{
name: config.viewConfigCatalog.CalendarLink,
value: "#CalendarLink.js-quickMenuElement"
}, {
name: config.viewConfigCatalog.ProductCreation,
value: "#ProductCreation.js-quickMenuElement"
}];
function getSelectorString(name) {
var i = kvp.length;
while (i--) {
var pair = kvp[i];
if (pair.name === name)
return pair.value;
}
return null;
}
function toggleMenuItems(config) {
// hide all our elements first
$(".js-quickMenuElement").hide();
config.data = config.data || [];
config.data.forEach(function(d) {
$(getSelectorString(d)).show();
});
};
document.writeln(getSelectorString(config.viewConfigCatalog.CalendarLink)+'<br/>');
document.writeln(getSelectorString(config.viewConfigCatalog.ProductCreation)+'<br/>');
document.writeln(getSelectorString("hi"));