I am using node.js. I have this function which uses promises to introduce delay between performing certain actions.
function do_consecutive_action() {
Promise.resolve()
.then(() => do_X() )
.then(() => Delay(1000))
.then(() => do_Y())
.then(() => Delay(1000))
.then(() => do_X())
.then(() => Delay(1000))
.then(() => do_Y())
;
}
What I want to do is to have this set of actions repeat itself forever. How can this be done in node.js?
//make following actions repeat forever
do_X()
Delay(1000)
do_Y()
Delay(1000)
EDIT: I started a bounty for answers that use a repeating queue to solve the problem.
Just use recursion
function do_consecutive_action() {
Promise.resolve()
.then(() => do_X() )
.then(() => Delay(1000))
.then(() => do_Y())
.then(() => Delay(1000))
.then(() => do_consecutive_action())
// You will also want to include a catch handler if an error happens
.catch((err) => { ... });
}
function cb(func) {
try {
func();
}
catch (e) {
do_consecutive_action();
}
}
function do_consecutive_action() {
Promise.resolve()
.then(() => cb(do_X))
.then(() => Delay(1000))
.then(() => cb(do_Y))
.then(() => Delay(1000))
.then(() => do_consecutive_action())
// You will also want to include a catch handler if an error happens
.catch((err) => { ... });
}
Related
I'm setting up a server in nodejs that uses kafka. In order to do that I'm using kafkaJS.
The problem here is that I don't want the server powering down everytime someone sends a post or reads a value from kafka.
Up until now I've tried this to read data
const readData = async (receiver, stream_name) => {
return new Promise((resolve, reject) => {
consumer.connect()
consumer.subscribe({ topic: stream_name })
.catch(e => {
console.log(stream_name)
console.log('failed to subscribe to topic')
console.log('err: ', e)
reject(e)
})
consumer.run({
eachMessage: async ({ topic, message }) => {
receiver(`- ${topic} ${message.timestamp} ${message.key}#${message.value}`)
resolve('ok')
}
})
.then(e => {
console.log('reading')
resolve('read')
})
.catch(e => {
console.log('messed up', e)
reject('fail')
})
setTimeout(() => {
console.log('egging')
return 0
}, 10000)
})
}
and this to create data
const pushData = async payload => {
return new Promise((resolve, reject) => {
producer.send(payload)
.then( e => {
resolve(e)
})
.catch(e => {
console.log('Error pushing data to kafka broker:\n', e)
reject(e)
})
})
}
// run ({String topic, {String key, JSON value[]} messages} payload)
const putData = async payload => {
console.log('Connecting to kafka server...')
await producer.connect()
const a = await pushData(payload)
console.log('Data sent: ', a)
await producer.disconnect()
process.abort()
}
This code works well but it needs to be put down in order to exit the method.
I wanted a solution in which I could either kill the process/thread where the kafka interface is executing or a regular exit of the method.
Any help is appreciated.
I am deploying this code on cloud functions, getting Cannot modify a WriteBatch that has been committed, I have tried committing after getting every collection but it's not a right way and is inconsistent, could not spot the error after trying several hours. Also the code run on first time after cold start , this post has same problem Batch write to firebase cloud firestore , where to create
a new batch for each set of writes.
in this code .
var batch = db.batch();
db.collection("myposts")
.doc(post_id)
.collection("fun")
.get()
.then(snapshot => {
return snapshot.forEach(doc => {
batch.delete(doc.ref);
});
})
.then(
db
.collection("relations_new")
.where("uid", "==", uid)
.get()
.then(snapshot => {
return snapshot.forEach(doc => {
batch.delete(doc.ref);
});
})
)
.then(
db
.collection("relation")
.where("uid", "==", uid)
.get()
.then(snapshot => {
return snapshot.forEach(doc => {
batch.delete(doc.ref);
});
})
.catch(err => {
console.log(err);
})
)
.then(
db
.collection("posts")
.doc(post_id)
.get()
.then(snap => {
return batch.delete(snap.ref);
})
.then(batch.commit())
)
.catch(err => {
console.log(err);
});`
Make sure to return promises from your then functions, and to ultimately return a promise from your cloud function. ESLint is a great tool for catching these kinds of errors.
let batch = db.batch();
return db.collection("myposts").doc(post_id)
.collection("fun").get()
.then(snapshot => {
snapshot.forEach(doc => {
batch.delete(doc.ref);
});
return null;
})
.then(() => {
return db.collection("relations_new")
.where("uid", "==", uid)
.get();
})
.then(snapshot => {
snapshot.forEach(doc => {
batch.delete(doc.ref);
});
return null;
})
.then(() => {
return db.collection("relation")
.where("uid", "==", uid)
.get();
})
.then(snapshot => {
snapshot.forEach(doc => {
batch.delete(doc.ref);
});
return null;
})
.then(() => {
return db.collection("posts").doc(post_id).get();
})
.then(snap => {
batch.delete(snap.ref);
return null;
})
.then(() => {
return batch.commit();
})
.then(() => {
console.log("Success");
return null;
})
.catch(err => {
console.log(err);
});
How to call "n" number of API calls in the sequentially order(each API response as input parameter for next API call) in the Node.js?
Please, review below example of promise in which you may find that it will execute synchronously as your requirement.
let firstPromise = (() => {
return new Promise((resolve) => {
resolve("first promise");
});
});
let secondPromise = (() => {
return new Promise((resolve) => {
resolve("second promise");
});
});
let thirdPromise = (() => {
return new Promise((resolve) => {
resolve("third promise");
});
});
let fourthPromise = (() => {
return new Promise((resolve) => {
resolve("fourth promise");
});
});
firstPromise()
.then((res) => {
console.log(res);
return secondPromise();
})
.then((res) => {
console.log(res);
return thirdPromise();
})
.then((res) => {
console.log(res);
return fourthPromise();
})
.then((res) => {
console.log(res);
})
.catch((err)=>{
throw new Error(err);
});
I am trying to download tracks via the soundcloud API, and then launch a callback once an indeterminant amount of tracks is downloaded. When I run the below code, I see "All done" being console logged before anything else, even though I intend for it to be the last thing... What am I doing wrong?
// Deps
import fs from 'fs'
import SC from 'node-soundcloud'
import request from 'request'
// Write mp3 function
function writeMP3(track) {
return new Promise((resolve, reject) => {
console.log('Starting download: ', track.title)
request.get(track.download_url)
.on('error', err => {
// reject('Download error: ', err)
})
.on('finish', () => {
() => resolve('Download complete')
})
.pipe(fs.createWriteStream(`./data/temp/${track.title}_${track.user.username}.mp3`))
})
}
async function asyncTrackFetch(track) {
return await writeMP3(track)
}
// Array of promises to callback upon
const trackActions = []
SC.init({
id: 'MY_ID',
secret: 'MY_SECRET'
})
SC.get('/tracks', (err, tracks) => {
if (err) {
throw new Error(err)
} else {
console.log('Tracks fetched: ', tracks.length)
tracks.map(track => {
if (track.downloadable) {
console.log('downloadable')
trackActions.push(asyncTrackFetch(track))
}
})
}
})
// Perform requests async
Promise.all(trackActions).then(() => {
console.log('All done')
console.log(fs.readdirSync('./data/temp'))
})
Promise.all(trackActions) waits on whatever promises are in trackActions, but trackActions is empty at the time you make the call. You're only adding promises to the array after your SC.get callback gets called.
Try putting your Promise.all... block inside the SC.get callback like this:
SC.get('/tracks', (err, tracks) => {
if (err) {
throw new Error(err)
} else {
console.log('Tracks fetched: ', tracks.length)
tracks.map(track => {
if (track.downloadable) {
console.log('downloadable')
trackActions.push(asyncTrackFetch(track))
}
})
Promise.all(trackActions).then(() => {
console.log('All done')
console.log(fs.readdirSync('./data/temp'))
})
}
})
It's worth mentioning as well that your line throw new Error(err) will crash the program since there's nowhere for that error to be caught.
As Antonio Val mentioned, there are better ways to do this. If you promisify the node-soundcloud library then the last part of your code could look like this:
SC.get('/tracks').then(tracks => {
// No need for trackedActions array.
return Promise.all(tracks.filter(track => track.downloadable)
.map(track => asyncTrackFetch(track)))
}).then(fetchedTracks => {
console.log('All done fetching tracks', fetchedTracks)
}).catch(err => {
// Handle error.
})
Or inside an async function,
try {
const tracks = await SC.get('/tracks')
const fetchPromises = tracks
.filter(track => track.downloadable)
.map(track => asyncTrackFetch(track))
const fetchedTracks = await Promise.all(fetchPromises)
console('All done fetching tracks.', fetchedTracks)
} catch (err) {
// Handle error
}
I think the easiest way would be to move Promise.all after tracks.map loop finished.
A more elegant solution would be to promisify SC.get as well and use async await along all your code.
UPDATE:
Couldn't test it so not sure if it works, but it would be something like this:
import fs from 'fs'
import SC from 'node-soundcloud'
import request from 'request'
function writeMP3(track) {
return new Promise((resolve, reject) => {
console.log('Starting download: ', track.title)
request.get(track.download_url)
.on('error', err => {
// reject('Download error: ', err)
})
.on('finish', () => {
() => resolve('Download complete')
})
.pipe(fs.createWriteStream(`./data/temp/${track.title}_${track.user.username}.mp3`))
})
}
function getTracks() {
return new Promise((resolve, reject) => {
SC.get('/tracks', (err, tracks) => {
if (err) {
return reject(err)
}
console.log('Tracks fetched: ', tracks.length)
resolve(tracks)
})
})
}
SC.init({
id: 'MY_ID',
secret: 'MY_SECRET'
})
With async await:
async function start() {
const tracks = await getTracks();
for (let track of tracks) {
await writeMP3(track)
}
}
start()
.then(() => {
console.log('All done')
console.log(fs.readdirSync('./data/temp'))
})
.catch((err) => {
// insert error handler here
})
If you just want to use Promises:
getTracks
.then((tracks) => {
const promiseArray = tracks.map((track) => {
return writeMP3(track)
})
return Promise.all(promiseArray)
})
.then(() => {
console.log('All done')
console.log(fs.readdirSync('./data/temp'))
})
.catch((err) => {
// insert error handler here
})
I am currently working on api with knex. I have to perform some operations in one big transaction and have to valdiate there also - if any validation returns "false" - transaction must be stoped. Problem is, whenever I toss "my" error there, even though all "Catch"es gets it and res is sended corrently- right after that my whole api crash with error:
Cannot read property "removeListener" of null
which is strange, becouse there are no similar issues with catching errors caused by knex itself.
Strangley, if i would remove throwing of my errors - i will still get unhandled exception
Cannot read property "rollback" of null
in code it looks like this:
f1(){
// (...)
let noErrors = true;
return global.knex
.transaction((trx) => {
return operation1(trx) //Knex operation returning object with parameter "correct"
.then((output)=>{
if(output.correct === false)
throw new Error('CustomError');
})
.then(()=>{ return operation2(trx); })
.then(()=>{ return operation3(trx); })
// (...)
.then(trx.commit)
.catch((error) => {
console.error('TRANS. FAILED');
noErrors = false;
trx.rollback();
throw error; // Both with and without it - failed
});
})
.then(() => {
console.log('TRANS. OK');
})
.then(() => {
if(noErrors)
return {result:'MyResultsHere'};
else
return {result:'ErrorOccured'};
})
.catch((error) => {
return {result:'ErrorOccuredAgain'};
});
}
This function's result (promise) is then returned :
f1().then((output)=>{
console.log(output.result);
// (...) sending response for request here
}
.catch((err) => {
console.error(err);
res.status(500).send();
});
AFter some additiona ltesting - it appears liek i can throw my custom errors, but issue here is with rollback - and soemtiems i get one more error:
TransactionError: Requests can only be made in the LoggedIn state, not
the SentClientRequest state
Looks like you are mixing 2 different transaction handling syntaxes (simple examples below):
knex.transaction(trx => {
// returning promise automatically calls commit / rollback
return operation(1);
})
.then(results => console.log("commit was called automatically", results))
.catch(err => console.log("rollback was called automatically", err))
and
knex.transaction(trx => {
// NOT returning promise so you need to call commit / rollback explicitly
operation(1).then(results => trx.commit(results))
.catch(err => trx.rollback(err));
})
.then(results => console.log("stuff from transaction commit", results))
.catch(err => console.log("error passed to rollback", err))
You are probably trying to do this:
f1(){
// (...)
return global.knex
.transaction(trx => {
return operation1(trx)
.then(output => {
if(output.correct === false) {
// if wrong results promise will reject with "CustomError"
throw new Error('CustomError');
}
})
.then(() => operation2(trx))
.then(() => operation3(trx))
// (...)
;
})
.then(resultsOfLastThen => {
console.log('TRANS. OK', resultsOfLastOperation);
return { result: 'MyResultsHere' };
})
.catch(error => {
return { result: 'ErrorOccured' };
});
}