Internal *current value* of Array - javascript

I'm creating an array that iterates asynchronously (for fun). This works fine:
class AsyncArray extends Array {
constructor() {
super();
this.x = 0;
}
[Symbol.iterator]() {
return {
next: () => {
let promise = new Promise(resolve => setTimeout(
() => resolve(this.x++), 1000)
);
return {done: this.x >= this.length, value: promise};
}
};
}
}
async () => {
for (let x of AsyncArray.of(1, 2, 3)) {
let value = await x;
console.log(value);
}
}();
However, this prints out 0...1...2 because I'm keeping track of the current counter on my own and initializing it to x.
Is there any way to get the current iterator value internal to the Array? I would also need to be able to properly determine the done value.

I guess you don't want the counter internal to your array, but rather to your iterator. Use a local variable in the method for that:
[Symbol.iterator]() {
var x = 0;
return {
next: () => {
let promise = new Promise(resolve =>
setTimeout(() => resolve(this[x++]), 1000)
);
return {done: x >= this.length, value: promise};
}
};
}
The easiest way to write iterators though is by using a generator function:
[Symbol.iterator]*() {
for (var x = 0; x < this.length; x++)
yield new Promise(resolve =>
setTimeout(() => resolve(this[x]), 1000)
);
}
That will take care of the correct done value as well (and won't "return" a promise that resolves with undefined).
An alternative that would completely avoid tracking state in a local variable or instance property would be to make use of the standard array iterator:
[Symbol.iterator]() {
var vals = super[Symbol.iterator]();
var it = Object.create(Object.getPrototypeOf(vals)); // an array iterator
it.next = () => {
var res = vals.next();
if (!res.done)
return {done: false, value: new Promise(resolve =>
setTimeout(() => resolve(res.value), 1000)
)};
return res;
};
return it;
}

Related

How to limit function calls in JS?

I need a function limitCalls (fn, maxCalls) that takes a function fn and returns a new function that can be called no more than the number of times specified in maxCalls. Test example:
it('limitCalls', () => {
const makeIncrement = () => {
let count = 0;
return () => {
count += 1;
return count;
};
};
const limitedIncrementA = limitCalls(makeIncrement(), 3);
expect(limitedIncrementA()).toBe(1);
expect(limitedIncrementA()).toBe(2);
expect(limitedIncrementA()).toBe(3);
expect(limitedIncrementA()).toBe(undefined);
expect(limitedIncrementA()).toBe(undefined);
const limitedIncrementB = limitCalls(makeIncrement(), 1);
expect(limitedIncrementB()).toBe(1);
expect(limitedIncrementB()).toBe(undefined);
expect(limitedIncrementB()).toBe(undefined);
});
I have:
var calls = 0;
export default function limitCalls(fn, maxCalls) {
if (calls >= maxCalls) {
return undefined;
}
calls += 1;
return fn();
}
And error is limitedIncrementA is not a function. Help me please to realise it.
Instead of conditionally returning a function, always return a function that conditionally executes the fn callback:
function limitCalls(fn, maxCalls) {
let count = 0;
return function(...args) {
return count++ < maxCalls ? fn(...args) : undefined;
}
}
const limited = limitCalls(console.log, 3);
limited('one');
limited('two');
limited('three');
limited('four');
In this snippet, limitedIncrementA isn't indeed a function. See this:
/* You're calling makeIncrement,
so you're passing its return to 'limitCalls'
*/
const limitedIncrementA = limitCalls(makeIncrement(), 3);
/* Here, considering that makeIncrement exists,
you're passing a reference to this functions,
which can be called inside 'limitCalls'
*/
const limitedIncrementB = limitCalls(makeIncrement, 3);
So, supposing that makeIncrement returns 1, 2, 3, ..., your current code is equivalent to:
limitCalls(1, 3);

how can I get the final result from a array of async ?

get-video-duration is a npm module that get the duration of video.
const { getVideoDurationInSeconds } = require('get-video-duration')
// From a local path...
getVideoDurationInSeconds('video.mov').then((duration) => {
console.log(duration)
})
I want to use this module to get the total duration of all videos from an Array of video pathes.
function getTotals(video_Array) {
let total_duration = 0;
video_Array.forEach(video => {
getVideoDurationInSeconds(video).then(duration => {
total_duration += duration;
})
})
}
The thing is getVideoDurationInSeconds is Asynchronous, I can't just simply return the result.
function getTotals(video_Array) {
let total_duration = 0;
video_Array.forEach(video => {
getVideoDurationInSeconds(video).then(duration => {
total_duration += duration;
})
})
return total_duration;
}
How can I get the final result? Thank you in advance!
create a function which returns a promise
and then use it to calculate total duration
function getTotals(video_Array) {
let video_ArrayPromises=video_Array.map(video=>
getVideoDurationInSeconds(video));
return Promise.all([video_ArrayPromises]).then((values) => {
//Calculate TotalDuration
return duratons.reduce((accumulator, currentValue) => accumulator + currentValue);
});
}
getTotals(['movie1.mov','movie2.mov']).then(totalDuration => {
//use total duration
});
Create an array of getVideoDurationInSeconds promises with map, then reduce over the values returned by Promise.all to get your final total.
Additional documentation
async/await
// Mock function that returns a promise.
// When it's resolved it will return a random number
// muliplied by the element passed in through the function arguments
function getVideoDurationInSeconds(el) {
const rnd = Math.floor(Math.random() * (10 - 1) + 1);
return new Promise((res, rej) => {
setTimeout(() => {
console.log(el * rnd);
res(el * rnd);
}, 1000);
});
}
async function getTotals(videoArray) {
// `map` over the elements of the video array
// and create a new array of promises
const promises = videoArray.map(el => getVideoDurationInSeconds(el));
// Wait until all the promises have resolved
const data = await Promise.all(promises);
// Then return the sum of each total in the data array
return data.reduce((acc, c) => acc += c, 0);
}
(async function main() {
console.log(`Total: ${await getTotals([1, 2, 3, 4])}`);
}());
Return an array of requests and use reduce to get the total time.
// Array
function getTotalTime() {
return videoList.map(async (video) => await getVideoDurationInSeconds(video));
}
// Just invoke whereever you want..
await Promise.all(getTotalTime()).then(result => {
let totalTime = result.reduce((acc, cv) => acc + cv, 0); // 0 = default value
})

JavaScript Async/Await: Unsuccessful with attempts to modify working code

I've written a short script using Async/Await that prints out letters one by one after short intervals. Based on what I understood to be happening, I tried rewriting the code in several ways expecting the same result, but I have been unable to make any of these alternatives work. In particular, I thought it would be straightforward to change where in the code the console.log() happens.
Here's the original working code:
const welcomeMessage = () => {
const message = 'hello'
const timer = [200,400,200,400,200,400];
// Promisify setTimeout() and feed in counter from sendMessage()
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout(resolve, timer[num]);
})
};
// Async/Await with a For loop calling setTimeoutPromise()
const sendMessage = async () => {
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
console.log(message[count]);
};
};
sendMessage();
}
welcomeMessage();
Then I tried to make a few modifications, none of which worked.
Mdofication #1: In this version, I thought I could just call and run the code in the sendMessage() function directly without needing to call it later. However, nothing happened after this modification:
async () => { //No name and removed call to sendMessage() later in code
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
console.log(message[count]);
};
};
const welcomeMessage = () => {
const message = 'hello'
const timer = [200,400,200,400,200,400];
// Promisify setTimeout() and feed in counter from sendMessage()
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout(resolve, timer[num]);
})
};
async () => { //No name and removed call to sendMessage() later in code
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
console.log(message[count]);
};
};
}
welcomeMessage();
Modification #2: I reverted the code and then tried to move the console.log() function into the setTimeout() function thinking this would be called on every loop. Both with empty ()'s and with (resolve) being passed into setTimeout(), it only printed the first letter. With (resolve, num) it says undefined:
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout((resolve) => {
console.log(message[num]);
resolve;
}, timer[num]);
})
};
const sendMessage = async () => {
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
};
};
const welcomeMessage = () => {
const message = 'hello'
const timer = [200,400,200,400,200,400];
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout((resolve) => {
console.log(message[num]);
resolve;
}, timer[num]);
})
};
const sendMessage = async () => {
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
};
};
sendMessage();
}
welcomeMessage();
Modification #3: Finally, I tried to define a function in advance to be passed into setTimeout() which would be used to handle "resolve" and console.log(). I tried a few variations and again didn't seem to be progressing through the loop as console.log() was only called once.
// New function to handle resolve and the counter
function newFunction(func, num) {
console.log(message[num]);
func;
}
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout(newFunction(resolve, num), timer[num]);
})
};
const sendMessage = async () => {
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
};
};
const welcomeMessage = () => {
const message = 'hello'
const timer = [200,400,200,400,200,400];
// New function to handle resolve and the counter
function newFunction(func, num) {
console.log(message[num]);
func;
}
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout(newFunction(resolve, num), timer[num]);
})
};
const sendMessage = async () => {
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
};
};
sendMessage()
}
welcomeMessage();
It appears to me, that you've started working with asynchrony before you got a deep understanding of how synchronous JavaScript works. Asynchrony is hard enough on its own too, so combined to that, it made you completely confused.
Let me explain what's going on and what's wrong in your snippets.
Let's start with the working one.
That code:
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout(resolve, timer[num]);
})
};
...creates a function named setTimeoutPromise, that:
takes an index (number) as its argument
returns a promise that:
after timer[num] milliseconds
resolves to undefined (setTimeout doesn't pass anything to its callback by default; in this case the callback is the resolve function)
The next part:
const sendMessage = async () => {
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
console.log(message[count]);
};
};
...defines an async function named sendMessage, that:
iterates over message, for each character:
calls setTimeoutPromise and awaits the promise it returns
after waiting, logs the current character to the console
Finally,
sendMessage();
...calls sendMessage, and therefore initiates the typing.
Now, let's move on to the next snippet.
This code:
async () => { //No name and removed call to sendMessage() later in code
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
console.log(message[count]);
};
};
...creates an async function, but it doesn't call or assign it to any variable: simply discards it.
To fix this snippet, call the function immediately by putting () after it!
const welcomeMessage = () => {
const message = 'hello'
const timer = [200,400,200,400,200,400];
// Promisify setTimeout() and feed in counter from sendMessage()
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout(resolve, timer[num]);
})
};
(async () => { //No name and removed call to sendMessage() later in code
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
console.log(message[count]);
};
})(); //The () at the end calls it
}
welcomeMessage();
Problematic snippet #2
There are 2 problems with this:
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout((resolve) => { //Problem 1
console.log(message[num]);
resolve; //Problem 2
}, timer[num]);
})
};
You try to take an argument named resolve from setTimeout, but as I mentioned above, it doesn't pass any.
To solve it, remove resolve from setTimeout((resolve) => {! We already have the resolve function from the above line, because of lexical scope.
You don't call resolve, that keeps the awaiting code hanging after the first letter (the promise never gets resolved).
To fix it, put () after resolve!
const welcomeMessage = () => {
const message = 'hello'
const timer = [200,400,200,400,200,400];
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout(() => {
console.log(message[num]);
resolve();
}, timer[num]);
})
};
const sendMessage = async () => {
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
};
};
sendMessage();
}
welcomeMessage();
Problematic snippet #3
There are 2 problems in this code as well:
// New function to handle resolve and the counter
function newFunction(func, num) {
console.log(message[num]);
func; //Problem 1
}
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout(newFunction(resolve, num), timer[num]); //Problem 2
})
};
The same as above; newFunction doesn't call resolve (named fn).
Try to not forget the () when you intend to call a function
That's the opposite of Problem 1. You immediately call newFunction (due to the parentheses after it: (resolve, num)), and pass its return value (undefined) to the setTimeout. Without Problem 1, this would result in immediately logging all letters.
In this case, let setTimeout to call that function internally by removing (resolve, num) after it. To pass parameters to it, setTimeout accepts additional arguments, that it will hand over to its callback (in this case newFunction).
const welcomeMessage = () => {
const message = 'hello'
const timer = [200,400,200,400,200,400];
// New function to handle resolve and the counter
function newFunction(func, num) {
console.log(message[num]);
func();
}
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout(newFunction, timer[num], resolve, num);
})
};
const sendMessage = async () => {
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
};
};
sendMessage()
}
welcomeMessage();
All together...
It is possible to combine these fixes, to get something like:
const welcomeMessage = () => {
const message = 'hello'
const timer = [200,400,200,400,200,400];
// New function to handle resolve and the counter
function newFunction(func, num) {
console.log(message[num]);
func();
}
const setTimeoutPromise = num => {
return new Promise(resolve => {
setTimeout(newFunction, timer[num], resolve, num);
})
};
(async () => {
for (count = 0; count < message.length; count++) {
await setTimeoutPromise(count);
};
})();
}
welcomeMessage();
Conclusion
Use parentheses (()) to call a function, but avoid them to use the function as an object: pass or assign it to something, get or set its properties, etc.

Creating a Timestamped Object Array for Sampling Data in Javascript?

Goal is to push sampled data, as an object, onto an array, at a periodic interval and wait to log the new array out to the console once it is finalized.
I'm new to JS, so take it easy ;). I am likely making this more complicated than it needs to be. Thought it would be as simple as a setTimeout() in a for loop.
I have been able to generate the array two different ways, using IIFE with a setTimeout() also the setInterval() below. Not sure how to get the async await function working with an array push() method querying length. Maybe this is not a good approach?
class Sample {
constructor(tag, timeStamp) {
this.tag = tag;
this.timeStamp = Date.now();
}
}
function arrayGenerator(tag){
return sampleArr.push(new Sample(tag));
};
function setIntSample(callback, delay, iterations) {
var i = 0;
var intervalID = setInterval(function () {
callback(i);
if (++i === iterations) {
clearInterval(intervalID);
}
}, delay);
};
Above seems to work console.log()-ing the array as it is generated in the arrayGenerator() function. Below, no dice
function resolveAfterArrGeneration(){
return new Promise(resolve => {
arrLength = setIntSample(i => {arrayGenerator(i)}, 3000, 5)
if (arrLength === 5) {resolve();}
});
}
async function ans() {
var answer = await resolveAfterArrGeneration();
console.log(sampleArr);
}
ans();
The basic idea is to return a promise and resolve the promise when the setInterval has run enough iterations. You can do that in a single function with something like this (with extra console.logs to show the process):
class Sample {
constructor(tag, timeStamp) {
this.tag = tag;
this.timeStamp = Date.now();
}
}
function makeSamples(iterations, delay){
let samples = [], i = 0;
return new Promise(resolve => {
let intervalID = setInterval(function () {
console.log("pushing new sample")
samples.push(new Sample('tag: ' + i));
if (++i === iterations) {
console.log("finished resolving")
clearInterval(intervalID);
resolve(samples)
}
}, delay);
})
}
makeSamples(5, 1000).then(console.log)
I would isolate the delay part (the asynchronous) part and create a separate, generic function delay() for that. All the rest becomes simple then, using an async function and for loop:
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
class Sample {
constructor(tag, timeStamp) {
this.tag = tag;
this.timeStamp = Date.now();
}
}
async function setIntSample(callback, ms, iterations) {
const arr = [];
for (let i = 0; i < iterations; i++) {
if (i) await delay(ms); // don't delay first time
arr.push(callback(i));
}
return arr;
}
const newSample = (tag) => new Sample(tag)
console.log("wait for it....");
setIntSample(newSample, 1000, 5).then(console.log);
Another way I just got working with a generator function
function* simpleGenerator(){
var index = 0;
while (true)
yield {tag: index++, time: Date.now()}
}
var gen = simpleGenerator();
..with the corresponding push
arr.push(gen.next().value);

Why my JavaScript function won't end?

So I have the following code, from which I expect the x function to return null after being called 3 times but it keeps returning the the same function:
const repeat = (n, tailFn) => {
for (let i = 0; i < n; i++) {
tailFn = () => tailFn;
}
return tailFn;
};
const x = repeat(2, x => null);
console.log(x()); // function tailFn() { return _tailFn }
console.log(x()()); // function tailFn() { return _tailFn }
console.log(x()()()()()()); // function tailFn() { return _tailFn }
What am I doing wrong? See it on CodePen.
Your function just assigns () => tailFn to tailFn three times and then returns it. Instead, you should return a function which returns repeat(n - 1, tailFn) if n is not 0, and tailFn otherwise.
const repeat = (n, tailFn) => {
return n !== 0 ? () => repeat(n - 1, tailFn) : tailFn;
};
const x = repeat(2, x => null);
console.log(x()); // () => repeat(n - 1, tailFn)
console.log(x()()); // x => null
console.log(x()()()); // null
You have created a function that ALWAYS returns itself,
tailFn=()=>tailFn;
actually the loop is meaningless.Its behavior is similar to a recursive function without a base case.

Categories

Resources