how do I create a nameless async function - Nodejs - javascript

I was wondering if it's possible to create a nameless function with the quick functional notation on javascript. What I mean by changing this:
var lobby = ((io) => {
...
}
return {
getWhiteBoardId: (name2, time2, role2, pass2, need_id) => {
let promise = new Promise((res, rej) => {
setTimeout(() => res("Now it's done!"), 1000)
});
// wait until the promise returns us a value
let result = await promise;
}
})(io);
I then want to later be able to call this function:
whiteboardId = lobby.getWhiteBoardId(req.body.name, req.body.time, req.body.role, req.body.pass, need_id);
to have something at the beginning such as this:
var lobby = (async (io) => {
so that I can call my Promise and await

// defining io, to execute the code, you not need to write the next line
const io = {};
const lobby = ((io) => {
// some pre-existing code
return {
// you are using await in this method so add async to the method signature
getWhiteBoardId: async (...args) => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(args), 1000)
});
// wait until the promise returns us a value
let result = await promise;
// ... some other code/logic as needed
// i have assumed you need to return the result
return result;
}
}
})(io);
// TEST: calling getWhiteBoardID method
lobby.getWhiteBoardId("Hi", "There").then(console.log);
// or
(async () => {
const res = await lobby.getWhiteBoardId("Hello World")
console.log(res);
})();
console.log("First!");

Related

Keep calling an API every 2.5 seconds and close the call once desired result is achieved

I have an API to call every 2.5 seconds. Initially, the data inside the response object is null as the database is still updating it through a transaction. But on the subsequent 3rd or 4th try, I get the data. I am writing a reusable function for the same, however I get undefined. My goal is to keep calling the API until I get the value in my path and close the connection. Please advice.
P.S: The below API URL doesnt have any delay, but my private API has.
const getData = (url, path) => {
const interval = setInterval(async () => {
const result = await axios.get(url);
if (_.has(result.data, path) && result.data[path]) {
return result[path]
}
}, 2500)
return clearInterval(interval)
}
getData('https://api.oceandrivers.com/static/resources.json', 'swaggerVersion')
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
JS Fiddle URL
Please advice.
You
return clearInterval(interval); // undefined
If you want to return a Promise which will resolve when the data is available, you could do something like this:
const getData = (url, path) => {
return new Promise((resolve, reject) => {
const interval = setInterval(async() => {
const result = await axios.get(url);
if (_.has(result.data, path) && result.data[path]) {
clearInterval(interval); // Clear the interval
resolve(result.data[path]); // Resolve with the data
}
}, 2500);
});
}
getData('https://api.oceandrivers.com/static/resources.json', 'swaggerVersion')
.then(data => {
console.log(data); // Your data is available here
});
// OR
(async () => {
const version = await getData('https://api.oceandrivers.com/static/resources.json', 'swaggerVersion');
console.log(version);
})();
Its because javascript is asynchronous as above comment is already mentioned. You can use either callbacks or promise in javascript. Here is the code:
const getData = (url, path, cb) => {
const interval = setInterval(async () => {
const result = await axios.get(url);
if (_.has(result.data, path) && result.data[path]) {
clearInterval(interval); //We found it remove the interval
cb(result.data[path]);
}
}, 2500);
};
getData(
"https://api.oceandrivers.com/static/resources.json",
"swaggerVersion",
data => {
console.log("test",data);
}
);
Here is the fiddle: https://jsfiddle.net/7pc4hq6t/3/
You could create an asynchronous delay:
const delay = milliseconds => new Promise(resolve, setTimeout(resolve, milliseconds));
Then use like this:
const getDataAsync = async (url, path) => {
while (true) {
const result = await axios.get(url);
if (_.has(result.data, path) && result.data[path]) {
return result.data[path];
}
await delay(2500);
}
}
const data = await getDataAsync('https://api.oceandrivers.com/static/resources.json', 'swaggerVersion');
This avoids the multiple layers of nested callbacks, and produces much more readable code.

Run multiple functions in succession while waiting for previous one to complete

I have a script which involves data processing and then uploading/downloading files in mass.
I've been trying to find different ways to sort out an issue I'm having and I've decided I want to try something like this (it appears to be the easiest solution), but I'm not sure if it's achievable with JavaScript.
I'm not that great with async and I have no clue what to do.
Thanks in advance, I've left a vague idea of what I'm doing below.
function one() {
>> do something | data processing
};
function two() {
>> do something | file upload/download
};
function thr() {
>> do something | process two arrays for an output that will be pushed to a global var
};
one();//wait for one to finish
two();//wait for two to finish
thr();//wait for thr to finish
function two() {
return new Promise( async (resolve, reject) => {
fs.createReadStream('data.csv')
.pipe(csv())
.on('data', (row) => {
if (row[removed] === '') return;
const
url1 = row[removed],
url2 = row[removed]),
https = require('https'),
id = row[removed]);
let upload = [];
const fetchRealUrl = request(url1, (e, response) => {//need to await completion
const FILENAMEREMOVED1= fs.createWriteStream(`${id}-FILENAMEREMOVED1`),
downloadRealFile = https.get(response.request.uri.href, function(response) {
response.pipe(FILENAMEREMOVED1);
console.log(`FILENAMEREMOVED file ${id} downloaded.`);
});
}),
fetchRealUrl2 = request(url2, (e, response) => {//need to await completion
const FILENAMEREMOVED2= fs.createWriteStream(`${id}-FILENAMEREMOVED2`),//need to extract into funciton instead of repeating
downloadRealFile2= https.get(response.request.uri.href, function(response) {
response.pipe(FILENAMEREMOVED2);
console.log(`FILENAMEREMOVEDfile ${id} downloaded.`);
});
});
//getData(url);
upload.push(`{"id":"${id}",REMOVED}`);
})
.on('end', (row) => {
console.info('Completed .');
resolve(upload);//completes while downloads still running
});
});
}
Try looking at Promises.
function one() {
return new Promise((resolve, reject) => {
//Do something...
let x = 10
resolve(x)
}
}
function two() {
return new Promise((resolve, reject) => {
//Do something else...
let y = 20
resolve(y)
}
}
one().then(x => { //The value resolved in one() is passed here
//then() is executed only when one() resolves its promise
return two()
}).then(y => { //The value resolved in two() is passed here
//etc...
})
const func1 = new Promise(res =>{
setTimeout(() => {
//do some asynchronous work
res()
}, 1000)
})
const func2 = new Promise(res =>{
setTimeout(() => {
//do some asynchronous work
res()
}, 1000)
})
//To run async functions in a waterfall pattern:
func1()
.then(resultOfFunc1 => {
//do something with resultOfFunc1
return func2()
})
.then(resultOfFunc2 => {
//do something with resultOfFunc2
})
//To run async functions in a parallel pattern:
let promiseFunctions = [func1(), func2()]
let result = Promise.all(promiseFunctions)
//result will be an array of resolved promises

How can I call series of Promise functions?

The requirement is finishing the current function before moving to the next call:
var current_data = something;
Run(current_data).then((data1) => {
Run(data1).then(data2 => {
Run(data2).then(data3 => {
// and so on
})
})
});
The example above is only possible if I know exactly how much data I want to get.
In order to make the nested promises part of promise chain, you need to return the nested promises.
Run(current_data).then((data1) => {
return Run(data1).then(data2 => {
return Run(data2).then .....
});
});
I'm gonna assume your data is paginated and you don't know how many pages there are, therefore you can use a while loop with await inside of an async function like so:
(async function() {
var currentData = someInitialData;
// loop will break after you've processed all the data
while (currentData.hasMoreData()) {
// get next bunch of data & set it as current
currentData = await Run(currentData);
// do some processing here or whatever
}
})();
You can use the async-await to make code more readable.
async function getData(current_data){
let data1 = await Run(current_data)
let data2 = await Run(data1);
let result = await Run(data2);
return result;
}
Calling the getData function
getData(data)
.then(response => console.log(response))
.catch(error => console.log(error));
Try to avoid nested promises. If you need to call a series of promises, which depend on the previous call's response, then you should instead chain then like the following following -
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('foo');
}, 1000);
});
promise1.then((response) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(response + ' b');
}, 1000);
});
}).then((responseB) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(responseB + ' c');
}, 1000);
});
}).then((responseC) => {
console.log(responseC); // 'foo b c'
})
if your code can support async-await then what Mohammed Ashfaq suggested is an alternative.
If you are executing the same function over and over again but on different data, I would make a recursive function that returns return a Promise.
I just look at my example below using an an array of numbers, you can edit it to your current case.
var current_data = [1,2,4,5,6]
function Run(data){
if(data.length === 0)
return Promise.resolve(data);
return new Promise((resolve, reject)=>{
//your async operation
//wait one second before resolving
setTimeout(()=>{
data.pop()
console.log(data)
resolve(data)
},1000)
})
.then((results)=>{
return Run(results)
})
}
Run(current_data)

await for indexdb event in async function

I'm trying to return a custom object from a async function that works as wrapper for a put using indexdb.
Using Promises this is easy.
However, using async/await became more challenging...
const set = async (storeName, key, value) => {
if (!db)
throw new Error("no db!");
try {
const result = {};
let tx = db.transaction(storeName, "readwrite");
let store = tx.objectStore(storeName);
let r = store.put({ data: key, value: value });
console.log(r);
r.onsuccess = async () => {
console.log('onsuccess');
result.something = true;
}
r.onerror = async () => {
console.log('onerror');
result.something = false;
}
await r.transaction.complete; // ok... this don't work
// how can I await until onsuccess or onerror runs?
return result;
} catch (error) {
console.log(error);
}
}
The ideia is to return a composed object... however all my attemps fails as onsuccess runs after returning the result.
I googled a lot and could't find a way to proper await for onsuccess/onerror events.
I know that returning a Promise is more easy as resolve(result) would end returning what I want... but i'm trying to learn to make same code using async/await.
Thank you so much,
Try this:
function set(db, storeName, key, value) {
return new Promise((resolve, reject) => {
let result;
const tx = db.transaction(storeName, 'readwrite');
tx.oncomplete = _ => resolve(result);
tx.onerror = event => reject(event.target.error);
const store = tx.objectStore(storeName);
const request = store.put({data: key, value: value});
request.onsuccess = _ => result = request.result;
});
}
async function callIt() {
const db = ...;
const result = await set(db, storeName, key, value);
console.log(result);
}
Edit, since you insist on using the async qualifier for the set function, you can do this instead. Please note I find this pretty silly:
async function set(db, storeName, key, value) {
// Wrap the code that uses indexedDB in a promise because that is
// the only way to use indexedDB together with promises and
// async/await syntax. Note this syntax is much less preferred than
// using the promise-returning function pattern I used in the previous
// section of this answer.
const promise = new Promise((resolve, reject) => {
let result;
const tx = db.transaction(storeName, 'readwrite');
tx.oncomplete = _ => resolve(result);
tx.onerror = event => reject(event.target.error);
const store = tx.objectStore(storeName);
const request = store.put({data: key, value: value});
request.onsuccess = _ => result = request.result;
});
// We have executed the promise, but have not awaited it yet. So now we
// await it. We can use try/catch here too, if we want, because the
// await will translate the promise rejection into an exception. Of course,
// this is also rather silly because we are doing the same thing as just
// allowing an uncaught exception to exit the function early.
let result;
try {
result = await promise;
} catch(error) {
console.log(error);
return;
}
// Now do something with the result
console.debug('The result is', result);
}
Ultimately you'll end up wrapping IDB in a promise-friend library, but for your specific need, you could use something like this:
function promiseForTransaction(tx) {
return new Promise((resolve, reject) => {
tx.oncomplete = e => resolve();
tx.onabort = e => reject(tx.error);
});
}
And then in your code you can write things such as:
await promiseForTransaction(r.tx);
... which will wait until the transaction completes, and throw an exception if it aborts. (Note that this requires calling the helper
before the transaction could possibly have completed/aborted, since
it won't ever resolve if the events have already fired)
I can't confirm it right now but I think it should be await tx.complete instead of await r.transaction.complete;.
But a general solution that would work even if the API would not support Promises directly would be to wrap a new Promise around the onsuccess and onerror and use await to wait for that Promise to resolve, and in your onsuccess and onerror you then call the resolve function:
const set = async (storeName, key, value) => {
if (!db)
throw new Error("no db!");
try {
const result = {};
let tx = db.transaction(storeName, "readwrite");
let store = tx.objectStore(storeName);
let r = store.put({
data: key,
value: value
});
console.log(r);
await new Promise((resolve, reject) => {
r.onsuccess = () => {
console.log('onsuccess');
result.something = true;
resolve()
}
r.onerror = () => {
console.log('onerror');
result.something = false;
// I assume you want to resolve the promise even if you get an error
resolve()
}
})
return result;
} catch (error) {
console.log(error);
}
}
I would furhter change it to:
try {
await new Promise((resolve, reject) => {
r.onsuccess = resolve
r.onerror = reject
})
console.log('success');
result.something = true;
} catch(err) {
console.log('error');
result.something = false;
}

Determine which promise is slowest in Promises.all

I have been using Promise.all in my app.
For the purpose of improving app speed, how to determine which promise is the slowest?
const result = await Promise.all([
this.props.fetchUser(),
this.props.cacheResourcesAsync(),
this.props.initAmplitude(),
this.props.initAppVariables(),
]);
You can use a helper function for that:
async function time(p, name) {
const start = Date.now();
try {
return await p;
} finally {
const end = Date.now();
console.log(`${name} took ${end-start}ms`);
}
}
Then write
const result = await Promise.all([
time(this.props.fetchUser(), "user"),
time(this.props.cacheResourcesAsync(), "cacheResources"),
time(this.props.initAmplitude(), "amplitude"),
time(this.props.initAppVariables(), "appVars"),
]);
I'd do something like this :
let startTime = new Date();
Promise.all([
this.fetchUser().then(() => {
console.log('fetch user takes', new Date().getTime()-startTime.getTime());
return arguments;}),
this.fetchData().then(() => {
console.log('fetchData takes', new Date().getTime()-startTime.getTime());
return arguments;})
]);

Categories

Resources