Functional javascript read, async, write result - javascript

I am reading mostly adequate guide to fp now. But I cannot figure out how to correctly compose this functions.
const R = require('ramda');
const {IO, Future} = require('ramda-fantasy');
const read = () => IO(() => 'hello');
const write = (data) => IO(() => {
console.log(data)
return data;
});
const process = (data) => Future((reject, resolve) => {
return setTimeout(() => resolve(data), 0);
});
What is the best way to combine functions: read -> process -> write ?

change the ios to futures.
const R = require('ramda');
const {Future} = require('ramda-fantasy');
const read = () => Future((reject, resolve) => resolve('hello'));
const write = (data) => Future((reject, resolve) => {
console.log(data)
resolve(data);
});
const process = (data) => Future((reject, resolve) => {
return setTimeout(() => resolve(data), 0);
});
read.chain(process)
.chain(write)
.fork(console.warn, console.log);
Futures are like async ios so theres not much point between lifting between the two types. If you really wanted to treat them differently you could nest the monad into IO> as bergi suggested.

Related

Chain async fallbacks and print their errors only if all fail

I want to invoke an async method, followed by a series of async fallback methods until one of them succeeds.
If all invocations fail, then I want all of their errors printed. Otherwise, if even one succeeds, the errors should not be printed.
This is what I want:
tryX()
.catch(x => tryXFallback1()
.catch(xf1 => tryXFallback2()
.catch(xf2 => tryXFallback3()
.catch(xf3 => tryXFallback4()
// ...
.catch(xf4 => Promise.reject([x, xf1, xf2, xf3, xf4]))))));
But I'm not a fan of the indentation. Accumulating the errors in a variable outside the scope of the catch clauses also seems messy:
let errors = [];
tryX()
.catch(x => {
errors.push(x);
return tryXFallback1();
})
.catch(xf1 => {
errors.push(x);
return tryXFallback2();
})
.catch(xf2 => {
errors.push(x);
return tryXFallback3();
})
.catch(xf3 => {
errors.push(x);
return tryXFallback4();
})
// ...
.catch(xf4 => Promise.reject(errors));
Lastly, I thought I could do some sort of for loop instead but that seems even uglier e.g.:
let methods = [tryX, tryFallback1, tryFallback2, tryFallback3, tryFallback4, /*...*/];
let errors = [];
for (let x of methods)
try {
return await x();
} catch (e) {
errors.push(e);
}
if (errors.length === methods.length)
return Promise.reject(errors);
Does anyone know of a more elegant approach?
The loop you have seems fine. I would probably stick with it as it already works. However, here is an alternative:
function tryWithFallbacks(main, ...fallbacks) {
return fallbacks.reduce(
(p, nextFallback) => p.catch( //handle errors
err => nextFallback() //try using the fallback
.catch(e => Promise.reject(err.concat(e))) //propagate rejection reasons
//on failure by adding to
//the array of errors
),
main() //seed the process with the main task
.catch(err => Promise.reject([err])) //ensure there is an array of errors
);
}
const a = tryWithFallbacks(
() => Promise.resolve(42)
);
test(a, "a"); //42
const b = tryWithFallbacks(
() => Promise.reject("oops"),
() => Promise.resolve("it's fine")
);
test(b, "b"); //"it's fine"
const c = tryWithFallbacks(
() => Promise.reject("oops1"),
() => Promise.reject("oops2"),
() => Promise.reject("oops3")
);
test(c, "c"); //["oops1", "oops2", "oops3"]
const d = tryWithFallbacks(
() => Promise.reject("oops1"),
() => Promise.reject("oops2"),
() => Promise.reject("oops3"),
() => Promise.resolve("finally!")
);
test(d, "d"); //"finally!"
const e = tryWithFallbacks(
() => Promise.reject("oops1"),
() => Promise.reject("oops2"),
() => Promise.reject("oops3"),
() => Promise.resolve("penultimate try successful!"),
() => Promise.reject("this is not reached")
);
test(e, "e"); //"penultimate try successful!"
//a simple function to illustrate the result
function test(promise, name) {
promise
.then(result => console.log(`[${name}] completed:`, result))
.catch(errorResult => console.log(`[${name}] failed:`, errorResult));
}
It's Array#reduce()-ing all the promises into one and making sure of the sequential order. If any succeed, you just get a single successful result. On failure, the error response is added to an array of all errors and passed forward via the rejection flow of promises.
This currently does require that all the functions that produce a promise are thunks - they take no input.
For reference, an equivalent operation using await and a loop would be:
async function tryWithFallbacks(...tasks) {
const errors = [];
for (const task of tasks) {
try {
const result = await task();
return result;
} catch (err) {
errors.push(err);
}
}
return errors;
}
const a = tryWithFallbacks(
() => Promise.resolve(42)
);
test(a, "a"); //42
const b = tryWithFallbacks(
() => Promise.reject("oops"),
() => Promise.resolve("it's fine")
);
test(b, "b"); //"it's fine"
const c = tryWithFallbacks(
() => Promise.reject("oops1"),
() => Promise.reject("oops2"),
() => Promise.reject("oops3")
);
test(c, "c"); //["oops1", "oops2", "oops3"]
const d = tryWithFallbacks(
() => Promise.reject("oops1"),
() => Promise.reject("oops2"),
() => Promise.reject("oops3"),
() => Promise.resolve("finally!")
);
test(d, "d"); //"finally!"
const e = tryWithFallbacks(
() => Promise.reject("oops1"),
() => Promise.reject("oops2"),
() => Promise.reject("oops3"),
() => Promise.resolve("penultimate try successful!"),
() => Promise.reject("this is not reached")
);
test(e, "e"); //"penultimate try successful!"
//a simple function to illustrate the result
function test(promise, name) {
promise
.then(result => console.log(`[${name}] completed:`, result))
.catch(errorResult => console.log(`[${name}] failed:`, errorResult));
}
Use Promise.any():
Promise.any([tryXFallback1, tryXFallback2, tryXFallback3]).then(result => console.log(result)).catch(err => console.log(err.errors))
This solves everything you need:
Works if any async of the passed array is resolved
Logs all of the errors that were thrown (The object that is passed to the catch callback of any, has property .errors)

How can I use a for loop to reiterate a promise function?

I have the following code that is used to get JSON data from an Amazon Web Server API.
var json1 = new Promise((resolve, reject) => {
fetch(url[0])
.then(r => {
resolve(r.json())
})
.catch(err => {
reject(err)
})
})
I have this repeating 14 times using different urls and json vars and have it return the promises at the end using.
return Promise.all([json1,json2,json3,json4,json5,json6,json7,json8,json9,json10,json11,json12,json13,json14]).then(function(values) {
return values;
});
This works, but it takes up 150+ lines. I want to make a for loop that runs through the same code using a for loop. I created this...
for(var jsonCount = 0;jsonCount<url.length-1;jsonCount++){
jsonArr[jsonCount] = new Promise((resolve, reject) => {
fetch(url[jsonCount])
.then(r => {
resolve(r.json())
})
.catch(err => {
reject(err)
})
})
}
This doesn't work because the promise functions come back as undefined even though it is called by an await function.
const data = await fetchURL(urlToQuery())
Does anyone have suggestions to make this work? There is JSON being returned.
Thanks for your help.
Edit: Here is a larger chunk of the code.
function fetchURL(urls) {
let fetchJson = url => fetch(url).then(response => response.json());
Promise.all(urls.map(fetchJson)).then(arr => {
return arr;
});
(async function() {
const data = await fetchURL(urlToQuery())
console.log(data);
for(var r=0;r<numStations;r++){
if (data[r] == ""){
onlineArr[r] = false;
wdDataArr[r].push(cardinalToDeg(stationHistAvgArr[r]));
wsDataArr[r].push(0);
You can use .map for the loop. But don't use new Promise. You don't need a new promise when fetch already provides you with one.
Also, call your array urls instead of url. A plural will be a good indication for the reader of your code that indeed it is a collection of URLs.
Here is how it could look:
let fetchJson = url => fetch(url).then(response => response.json());
Promise.all(urls.map(fetchJson)).then(arr => {
// process your data
for (let obj of arr) {
console.log(obj);
}
});
I think this example can helps you:
// Mock async function
const getDataAsync = callback => {
setTimeout(
() => callback(Math.ceil(Math.random() * 100)),
Math.random() * 1000 + 2000
)
}
// Create the promise
const getDataWithPromise = () => {
return new Promise((resolve, reject) => {
try {
getDataAsync(resolve);
} catch(e) {
reject(e);
}
});
}
// Using the promise one time
getDataWithPromise()
.then(data => console.log("Simple promise:",data))
.catch(error => console.error(`Error catched ${error}`));
// Promises compound: Promise.all
const promise1 = getDataWithPromise();
promise1.then(data => console.log("promise1 ends:",data));
const promise2 = getDataWithPromise();
promise2.then(data => console.log("promise2 ends:",data));
const promise3 = getDataWithPromise();
promise3.then(data => console.log("promise3 ends:",data));
const promise4 = getDataWithPromise();
promise4.then(data => console.log("promise4 ends:",data));
const promise5 = getDataWithPromise();
promise5.then(data => console.log("promise5 ends:",data));
Promise.all([promise1,promise2,promise3,promise4,promise5])
.then(data => console.log("Promise all ends !!",data));
Hope this helps
you will have issues with closure and var variable capture.
You may want to change var to let to capture the right value in the closure so that url[jsonCount] is actually what you want.
also I think it would be much easier to do something like that in one line :)
let results = [];
for(let i = 0; i < urls.length; ++i) results.push(await (await fetch[urls[i]]).json());
This is a good use for map, mapping urls to promises...
function fetchUrls(urls) {
let promises = urls.map(url => fetch(url))
return Promise.all(promises).then(results => {
return results.map(result => result.json())
})
}}
// url is your array of urls (which would be better named as a plural)
fetchUrls(url).then(results => {
// results will be the fetched json
})
Using the async/await syntax (equivalent meaning)
// this can be called with await from within another async function
async function fetchUrls(urls) {
let promises = urls.map(url => fetch(url))
let results = await Promise.all(promises)
return results.map(result => result.json())
}

how create multiple parsing with axios

How to create a request queue? if you build sequential queries, then they are called simultaneously
const s1 = async () => {
axios(url_benzinga)
.then(response => {
var html = response.data;
var $ = cheerio.load(html)
const s2 = async () => {
axios(url_benzinga)
.then(response => {
var html = response.data;
var $ = cheerio.load(html)
I can not understand how to do it right
https://ibb.co/ngPr45p
https://github.com/axios/axios/issues/371
It would look something like:
axios.get('http://google.com')
.then((res) => {
// do something with Google res
return axios.get('http://apple.com');
})
.then((res) => {
// do something with Apple res
})
.catch((err) => {
// handle err
});
Alternatively you can send both requests simultaneously and handle responses at the same time:
axios.all([
axios.get('http://google.com'),
axios.get('http://apple.com')
])
.then(axios.spread((googleRes, appleRes) => {
// do something with both responses
});
i think this is not the right solution
axios.get('api')
.then(res => {
// first save this api response anywhere. then after that particular action call next api otherwise i think it will be complex or sometime axios . then return an error so should also aware from such error
})
.catch(err => console.log(err.message))
It is easy with async/await syntax.
Example, you have 2 requests like:
const s1 = async () => {
return axios.get('http://google.com');
}
const s2 = async () => {
return axios.get('http://apple.com');
}
Now, if you want to take request in sequential
// sequential queries,
const res1 = await s1();
const res2 = await s2();
// do something with both responses
and, request in parallel
const [res1, res2] = await Promise.all([
s1(),
s2(),
]);
// do something with both responses

How to mock objects which return a promise?

I want to create a mock of this object for unit testing purposes.
sqs.listQueues().promise()
.then(...
Here's one of my attempts at mocking this and still getting the error sqs.listQueues(...).promise is not a function
const sqs = {
listQueues: () => Promise.resolve(this),
promise: () => Promise.resolve()
}
How can i properly mock this object?
Looks like listQueues() should not return a promise. Maybe this would work:
const sqs = {
listQueues: () => ({
promise: () => Promise.resolve()
})
}
Here's a fairly naive version that might get you going.
const mockResolve = (val, delay = 0) => () =>
new Promise((res) => setTimeout(() => res(val), delay))
const mockReject = (err, delay = 0) => () =>
new Promise((_, rej) => setTimeout(() => rej(err), delay))
const sqs = {
listQueues: () => ({
promise: mockResolve('foo')
})
}
sqs.listQueues().promise().then(console.log)
There are probably many things wrong with this, but it's only meant as a first pass.

Assign return value of function to variable

I kind of got the same question as has been asked here.
I've made a function:
const rewardStayingViewersOrNewcomers = () => {
fetch('https://tmi.twitch.tv/group/user/instak/chatters')
.then(parseJSON)
.then(r => {
let chatters = r.chatters;
viewerKeys = Object.keys(chatters); // [mods, viewers,...]
let ChattersPerRole = viewerKeys.map(role => {
return chatters[role].map(username => ({
username, role
}));
});
return flattenDeep(ChattersPerRole);
}).catch(err => {
console.log(`Error in fetch: ${err}`);
});
};
Why can't I assign that return value to my variable? The log of that variable returns undefined...
let viewersPerRole = rewardStayingViewersOrNewcomers();
setTimeout(() => console.log(viewersPerRole), 7000);
Bonus question, how could I easily wait for viewersPerRole to be filled with the data I'm waiting for because of the fetch? (so I don't have to use setTimeout())?
First of all, try returning something from the main function. There's no return in front on the fetch(...). Code should look like:
const rewardStayingViewersOrNewcomers = () => {
return fetch('https://tmi.twitch.tv/group/user/instak/chatters')
.then(parseJSON)
.then(r => {
let chatters = r.chatters;
viewerKeys = Object.keys(chatters); // [mods, viewers,...]
let ChattersPerRole = viewerKeys.map(role => {
return chatters[role].map(username => ({
username, role
}));
});
return Promise.resolve(flattenDeep(ChattersPerRole));
}).catch(err => {
console.log(`Error in fetch: ${err}`);
});
};
// now that you're returning a promise
rewardStayingViewersOrNewcomers()
.then(viewersPerRole => console.log(viewersPerRole))
If you're using Babeljs to transpile with stage3 enabled, have a look at async / await.

Categories

Resources