Async version of sort function in JavaScript - javascript

Found this question Implement async/await in sort function of arrays javascript but mine is different.
I want to implement async version of the sort function. for my programming language that compiles to JavaScript called Gaiman where everything is async by default. This make the code way simpler. Some things can be really asynchronuios like reading the input from user but all functions defined in Gaiman are async even if they don't use anything that is really asynchronous.
I've implemented custom class that extend Array:
function is_function(obj) {
return typeof obj === 'function';
}
function is_promise(obj) {
return obj && is_function(obj.then);
}
class GaimanArray extends Array {
map(...args) {
function call(arr) {
return new GaimanArray(...arr);
}
const arr = super.map.apply(this, args);
const some = super.some;
const has_promise = some.call(arr, is_promise);
if (has_promise) {
return Promise.all(arr).then(call);
} else {
return call(arr);
}
}
forEach(...args) {
return this.map(...args);
}
filter(fn, ctx) {
const filter = super.filter;
function call(arr) {
return new GaimanArray(...filter.call(arr, x => x));
}
const items = this.map(fn, ctx);
if (is_promise(items)) {
return items.then(arr => {
return call(arr);
});
} else {
return call(items);
}
}
reduce(fn, init) {
return super.reduce.call(this, function(acc, ...args) {
if (is_promise(acc)) {
return acc.then(acc => {
return fn(acc, ...args);
});
} else {
return fn(acc, ...args);
}
}, init);
}
sort() {
}
some(fn, ctx) {
const some = super.some;
return this.mapWithCallback(fn, (arr) => {
return some.call(arr, x => x);
}, ctx);
}
every(fn, ctx) {
const every = super.every;
return this.mapWithCallback(fn, (arr) => {
return every.call(arr, x => x);
}, ctx);
}
find(fn, ctx) {
return this.mapWithCallback(fn, (arr) => {
const index = arr.findIndex(x => x);
return this[index];
}, ctx);
}
flatMap(fn, ...args) {
return this.map(...args).flat();
}
mapWithCallback(fn, callback, ctx) {
const items = this.map(fn, ctx);
if (is_promise(items)) {
return items.then(arr => {
return callback(arr);
});
} else {
return callback(items);
}
}
}
The implementation of methods in my array actually makes the promise-based function optional, this is a leftover implementation when I was modifying Array.prototype but it gives problems with some other library that was executing args.some(is_promise).
I think that I can't use Schwartzian transform because I can't get value for each item, my comparison function needs to be really async.
So how can I implement async sort in JavaScript? Every method is working with async functions except I have no idea how to implement sort.

I've created working code based on Rosseta Code:
// based on: https://rosettacode.org/wiki/Sorting_algorithms/Merge_sort#JavaScript
async function mergeSort(array, fn) {
if (array.length <= 1) {
return array;
}
const mid = Math.floor(array.length / 2),
left = array.slice(0, mid), right = array.slice(mid);
await mergeSort(left, fn);
await mergeSort(right, fn);
let ia = 0, il = 0, ir = 0;
while (il < left.length && ir < right.length) {
array[ia++] = (await fn(left[il], right[ir]) <= 0) ? left[il++] : right[ir++];
}
while (il < left.length) {
array[ia++] = left[il++];
}
while (ir < right.length) {
array[ia++] = right[ir++];
}
return array;
}

Related

Method chaining with Javascript - Plus Minus Question

I got a code challenge that create a plus-minus functions using method chaining. i have created the code as follows but it eventually failed when it comes to the output rendering
like plus(3).minus(2).value() and minus(3).minus(3).value() kind of method invoking
code as follows
function plus(valuez)
{
this.valuez = this.valuez+ valuez;
function value{
return valuez
}
plus.minus = minus;
plus.value = value;
plus.plus = this;
return this;
}
function minus(valuez)
{
this.valuez = this.v+ valuez;
function value(){
return valuez
}
minus.plus = plus;
minus.minus = this
minus.value = value;
return this;
}
expected output is
1 and 6 but I only get the printed last number entered. how can I resolve this?
class Box {
constructor(v) { this._value = v }
plus(v) { this._value += v; return this; }
minus(v) { this._value -= v; return this; }
value() { return this._value; }
}
function plus(v) { return new Box(v) }
function minus(v) { return new Box(-v) }
console.log("plus(3).minus(2).value()", plus(3).minus(2).value());
console.log("minus(3).minus(3).value()", minus(3).minus(3).value());
function plus (x) { return { _value: x, plus(y){ return plus(this._value+y) }, minus(y){ return plus(this._value-y) }, value(){ return this._value } } }
function minus(x) { return plus(-x) }
console.log("plus(3).minus(2).value()", plus(3).minus(2).value());
console.log("minus(3).minus(3).value()", minus(3).minus(3).value());
Using closure
function plus (x) { return { plus(y){ return plus(x+y) }, minus(y){ return plus(x-y) }, value(){ return x } } }
function minus(x) { return plus(-x) }
console.log("plus(3).minus(2).value()", plus(3).minus(2).value());
console.log("minus(3).minus(3).value()", minus(3).minus(3).value());
Without a constructor or class-sugar you can return an object using closed over values from the addition/subtraction method. Something like:
const { plus, val, reset } = PM();
console.log(plus().plus(3).minus(4).plus(25).plus(4).trace());
console.log(reset().minus(2).plus(33).val());
function PM() {
let values = [];
let traced = [];
const reset = () => {
values = [];
traced = [];
return ret;
};
const add = value => {
const calc = +(values[values.length-1] || 0) + value;
traced.push(`${values[values.length-1] || 0}${
!value || value >= 0 ? "+" : ""}${value || 0}=${calc || 0}`);
values.push(calc || 0);
return ret;
};
const ret = {
plus: value => add(value),
minus: value => add(-value),
val: () => values,
trace: () => traced,
reset,
};
return ret;
}
Or combine this with an embedded constructor

How to refactor "if x return x" statements

I am looking for a way to refactoring this "if x return x" pattern. It seems to be redundant.
For example:
async exampleFunction(a) {
const firstResult = await this.firstHandler(a);
if (firstResult) {
return firstResult;
}
const secondResult = await this.secondHandler(a);
if (secondResult ) {
return secondResult;
}
const thirdResult = await this.thirdHandler(a);
if (thirdResult) {
return thirdResult;
}
}
function exampleFunction(a) {
return this.firstHandler(a) || this.secondHandler(a);
}

Chain functions in JavaScript

Is there a way to chain functions in JavaScript so when last function in chain is called we take into consideration all function in chain that was specified.
Basically what I am trying to do is the same thing express-validator
does:
Something like this:
check('password').passwordValidator().optional();
I want to be able to call
check('password').passwordValidator();
and
check('password').passwordValidator().optional();
So you're looking for a sort of builder pattern? You can do that like this:
class Foo {
_passwordValidator = false;
_optional = false;
passwordValidator() {
this._passwordValidator = true;
return this;
}
optional() {
this._optional = true;
return this;
}
doThing() {
if (this._optional) { /* ... */ }
if (this._passwordValidator) { /* ... */ }
}
}
const foo = new Foo().passwordValidator().optional();
foo.doThing();
Edit: to more directly answer your question, there is no way to wait to do something until the current chain of method calls is done; you have to call a method like doThing() in the example to signal that you actually want to do the thing now.
I ended up using what #coolreader18 suggested.
That was exactly what I was looking for.
function func(val) {
this._optional = false;
this._check = false;
const doStaff = (message = 'Doing staff') => {
console.log(message);
return;
};
return {
check: function(n) {
this._check = true;
return this;
},
optional: function(n) {
this._check = false;
this._optional = true;
return this;
},
exec: function() {
if (this._check) doStaff();
if (this._optional) doStaff('Maybe not');
}
}
}
func().check().optional().exec();
var Obj = {
result: 0,
addNumber: function(a, b) {
this.result = a + b;
return this;
},
multiplyNumber: function(a) {
this.result = this.result * a;
return this;
},
divideNumber: function(a) {
this.result = this.result / a;
return this;
}
}
Obj.addNumber(10, 20).multiplyNumber(10).divideNumber(10);
link => https://medium.com/technofunnel/javascript-function-chaining-8b2fbef76f7f
Calling a chained method of express-validator returns a middleware function, and as functions can have properties you can call a method on that returned function, which returns a new function with methods and so on. Chaining functions is quite easy:
const chain = (pairs, fn = el => el) => {
for(const [key, method] of pairs)
fn[key] = (...opt) => chain(pairs, method(fn)(...opt));
return fn;
};
const math = chain([
["add", prev => a => b => prev(b) + a],
["mul", prev => a => b => prev(b) * a]
]);
console.log(
(math.add(5).mul(3).add(3))(5)
);
This solution is inspierd by React setState:
function pipe/*<U>*/(val/*: U*/) {
return {
case: function (condition/*: boolean*/, value/*: U | ((prop: U) => U)*/) {
if (condition) {
if (value instanceof Function) {
return pipe(value(val));
} else {
return pipe(value);
}
} else {
return pipe(val);
}
},
valueOf: function () {
return val;
},
};
}
const result = pipe(2)
.case(false, 3)
.case(true, (current) => current + 2)
.case(false, 4)
.valueOf();
console.log(result) // 4

Chaining Promises Cross Function Javascript

I suspect I've fundementally misunderstood Javascript promises, any ideas?
I have a pretty function that queries a database containing music that looks like this:
function searchDatabaseForTrack(query,loadedResults){
loadedResults = loadedResults || [];
desiredResults = 100;
if (loadedResults.length < desiredResults) {
try {
databaseApi.searchTracks(query, {"offset":loadedResults.length, "limit":"50", }).then(function(data){
i=0
if (data.tracks.items.length == 0) {
console.log(`Already loaded all ${loadedResults.length} tracks!`)
console.log(loadedResults)
return loadedResults;
}
else {
for (thing in data.tracks.items){
loadedResults.push(data.tracks.items[i]);
i=i+1;
}
console.log(loadedResults.length, " tracks collected");
searchDatabaseForTrack(query,loadedResults)
}
});
} catch(err) {
console.log("ERROR!", err)
console.log(loadedResults)
return loadedResults;
}
} else {
console.log(loadedResults)
return loadedResults;
}
}
And then a bit later, I try to call and use the data retrieved.
function getArtistTracks(artistName){
searchDatabaseForTrack(artistName).then(function(data){
console.log(songs);
songs.sort(function(a,b){
var c = new Date(a.track.album.release_date);
var d = new Date(b.track.album.release_date);
return d-c;
});
console.log("songs", songs);
var newsongs=[];
i=0
for (song in songs) {
newsongs.push(songs[i].track.uri);
i++
};
return newsongs;
});
}
What I'm trying to do is get the second function "getArtistTracks" to wait for the completion of the query in the first function. Now I could just call the databaseApi.searchTracks directly, but there's a limit of 50 tracks returned per result — which kind of screws me over.
searchDatabaseForTrack().then(...) shouldn't work since searchDatabaseForTrack() doesn't return a promise, so you can either return a promise or use an async function.
instead of a recursive function, you could simply call databaseApi in a for loop,
the desiredResult should be an argument and not hardcoded in the function,
async function searchDatabaseForTrack(query, desiredResults){
let loadedResults = [], data, currOffset = 0;
const iterations = Math.ceil(desiredResults / 50);
for(let n = 0 ; n < iterations; n++){
cuurOffset = n * 50;
data = await databaseApi.searchTracks(query, {"offset":currOffset, "limit":"50", });
if (data.tracks.items.length == 0) {
console.log(`Already loaded all ${loadedResults.length} tracks!`)
console.log(loadedResults)
return loadedResults;
}
else {
loadedResults = loadedResults.concat(data.tracks.items);
console.log(loadedResults.length, " tracks collected");
}
}
return loadedResults;
}
the rest should be fine as long as you add .catch() to handle errors ( as mentionned in previous answer ) which are thrown automatically without the need of the try/catch block :
function getArtistTracks(artistName, 100){
searchDatabaseForTrack(artistName).then((songs) => {
// your previous code
})
.catch((err) => {
// handle error
});
});
Have searchDatabaseForTrack use Promise.all to return the loadedResults after all results have been gotten. Also, make sure not to implicitly create global variables as you're doing with thing. For example, try something like this:
async function searchDatabaseForTrack(query) {
const desiredResults = 100;
const trackPromises = Array.from(
({ length: Math.ceil(desiredResults / 50) }),
(_, i) => {
const offset = i * 50;
return databaseApi.searchTracks(query, { offset, limit: 50 });
}
);
const itemChunks = await Promise.all(trackPromises);
const loadedResults = itemChunks.reduce((a, { tracks: { items }}) => (
[...a, ...items]
), []);
return loadedResults;
};
and
searchDatabaseForTrack(artistName).then((loadedResults) => {
// do stuff with loadedResults
})
.catch((err) => {
console.log("ERROR!", err)
// handle error
});

NodeJS Async - Use it like for-cycle

How to use NodeJS Async (https://caolan.github.io/async/) as normal for-cycle?
for(var i = 0; i < 100; i++){
doSomething();
}
I need serial flow but normal for-cycle not waiting until action is finished.
I am open to ES6 solutions, if any.
You can try to use Async/Await from ES6 which is cleaner and common shared in many standards. And in addition you don't need any dependencies from third party
const forLoopiNSeria = async () => {
for(var i = 0; i < 10; i++){
console.log(await doSomething(i))
}
}
function doSomething(index) {
return new Promise( (resolve, reject) => {
setInterval(() => resolve(index), 500)
})
}
forLoopiNSeria()
I believe you could always do something like this:
function asyncWhile(condition, action, ctx) {
const whilst = function(data) {
return condition.call(ctx, data) ?
Promise.resolve(action.call(ctx, data)).then(whilst) :
data;
}
return whilst();
}
let i = 1
asyncWhile(
() => {
if (i <= 100) {
i += 1
return true
}
return false
},
() => {
console.log(`iteration ${i}`)
},
)
I found another way with usage of until function from async:
var i = 0;
var max = 100;
async.until(() => {
i++;
return i === max-1;
}, function (callback) {
//doMagic()
callback();
}, function () {
console.log("Finished");
});

Categories

Resources