JavaScript: Retry asynchronous readFile() function - javascript

I want to read a file with javascript. This file has to be downloaded first and is not immediately available. That means if an access to this file with readFile() fails and ends up in the catch block, then this action should be repeated.
const fse = require('fs-extra')
let retries = 0
function read_a_file(path) {
return fse.readFile(path)
.catch(error => {
if (retries < 5) {
retries++
console.log('Retry', retries)
setTimeout(() => read_a_file(path), 1000);
} else {
console.log(error.message)
}
})
}
read_a_file('path/to/file').then(content => {
console.log('Content:', content)
})
The output of this function is:
Retry 1
Content: undefined
Retry 2
Retry 3
Retry 4
Retry 5
ENOENT: no such file or directory, open 'path/to/file'
I would now have expected the content (in this case undefined) to be output at the end of all retries. Why is it issued before the first repetition?

You are creating new promises in the setTimeout that have nothing to do with the initial promise you returned.
I would creae a Promise and return that. The readFile would call that promise's resolve or reject.
const fse = require('fs-extra')
function read_a_file(path) {
let retries = 0
const readFile(resolve, reject) {
fse.readFile(path)
.then(resolve)
.catch(error => {
if (retries < 5) {
retries++
console.log('Retry', retries)
setTimeout(() => readFile(resolve, reject), 1000);
} else {
console.log(error.message)
reject(error);
}
})
}
return new Promise((resolve, reject) => readFile(resolve, reject));
}
read_a_file('path/to/file')
.then(content => {
console.log('Content:', content)
})
.catch(e => {
console.log(e);
})

You are using .then in the first call only, so the content logging function will only work once.
read_a_file('path/to/file').then(content => {
console.log('Content:', content)
})
Your other call to the function is setTimeout(() => read_a_file(path), 1000);, if you add a .then after that as well, it will also log it after the other promises resolve. You'll get 5 of those outputs then.

Related

Promise data and exception handling

I am confused with the use of promise, specifically of its way of data manipulation (passing values from block to block) and exception handling (bubbling up the error). I am trying to learn a right way to use promise and to handle error, something like
Error: A caught error.
at promiseTwo()
at promiseOne()
at subprocess()
at mainprocess()
Here are my two attempts in implementing them:
Attempt 1: Clumsy, deeply nested, and errors are uncaught.
var subprocess = () => {
return new Promise((resolve, reject) => {
promiseOne().then(data1 => {
// Some code with data1, throw some error
promiseTwo().then(data2 => {
// Some code with data1n2, throw some error
promiseThree().then(data3 => {
// Data manipulation with data1, data2, and data3
return resolve(<...>)
}).catch(err3 => { throw err3 })
}.catch(err2n3 => { throw err2n3 }) // >>> ERR: Cannot get err3.
}.catch(err1n2n3 => { return reject(err1n2n3) }) // >>> ERR: Cannot get err3 or err2.
}
}
return new Promise((resolve, reject) => {
subprocess().then(data => {
// TODO
}).catch(allErr => { return reject(allErr) }
}
Attempt 2: Unable to use data from previous promise block.
var subprocess = () => {
return new Promise((resolve, reject) => {
promiseOne()
.then(data1 => {
// Some code with data1, throw some error
return promiseTwo()
})
.then(data2 => {
// Some code with data1n2, throw some error
// >>> ERR: Cannot get data1
return promiseThree()
})
.then(data3 => {
// Data manipulation with data1, data2, and data3
// >>> ERR: Cannot get data1 and data2
return resolve(<...>)
})
.catch(err1n2n3 => {
return reject(err1n2n3)
})
}
}
return new Promise((resolve, reject) => {
subprocess().then(data => {
// Some code, throw some error
}).catch(allErr => { return reject(allErr) }
}
Note: Some of the promise block (i.e. promiseOne, promiseTwo, etc.) are pre-defined so I do not have control over what data they will return. I am sure there are more errors in the attempts (e.g. if returning a function is a right way to do it).
Please help. Thanks.
for this kind of situation, you can combine promises and async-await together.
From the question, it seems we have three promises and one function that executes and handle them.
You can try something like this -
const subProcess = () => {
return new Promise((resolve, reject) => {
// Using IIFE ( You shouldn't put async keyword on promise callbac )
(async () => {
// Use of try catch to handle the errors
try {
await promiseOne()
await promiseTwo()
await promiseThree()
// Additional code if need after them
} catch(err){
// Handle error ( all three promise error will be transferred here )
}
})()
})
}
The above code waits for the promises to execute one by one and also catch error from all three promises if any.
And as #samuei mentioned, you can also use Promise.all() in this.
const subProcess = () => {
return new Promise((resolve, reject) => {
// Using IIFE ( You shouldn't put async keyword on promise callbac )
(async () => {
// Use of try catch to handle the errors
try {
const myPromises = [promiseOne, promiseTwo, promiseThree];
const res = await Promise.all(myPromises);
// Additional code if need after them
} catch(err){
// Handle error ( all three promise error will be transferred here )
}
})()
})
}
And if you don't want to use async-await then you can do something like this as well
const subProcess = () => {
return new Promise((resolve, reject) => {
const myPromises = [];
const myPromises = [promiseOne, promiseTwo, promiseThree];
Promise.all(myPromises)
.then(res => {
// Handle the response
})
.catch(err => {
// Handle the error
})
})
}
It sounds like you're looking for Promise.all, which lets you set a series of promises in motion, then deal with the results when they are all resolved.

Resolve promises sequentially and break the sequence if one of the promises in the sequence throws an error [duplicate]

This question already has answers here:
chain promises in javascript
(2 answers)
Resolve promises one after another (i.e. in sequence)?
(36 answers)
Closed 2 years ago.
Let's say I have three async functions set as below:
const stepOne = async () => { setTimeout(function() {
console.log("step 1")
}, 3000) }
const stepTwo = async () => { throw new Error("Error at step two") }
const stepThree = async () => { console.log("step 3") }
How will I execute all these functions sequentially and break the promise chain at stepTwo not allowing stepThree function to run ever?
So,
normal sequence is like this: stepOne --> stepTwo --> stepThree
sequence with error thrown at stepTwo: stepOne --> stepTwo
The error thrown at stepTwo needs to get caught at end catch block.
UPDATE #1: Missed a crucial element of the question. await cannot be used as these three functions need to be called within a function which is not async.
example:
const testFunc = () => {
resolve three promises
sequentially, break the promise chain when error is thrown
and ultimately, catch errors here
}
Your code would work if you would resolve the promise from stepOne because setTimeout just adds the function to the stack and is not waiting for it to resolve.
If you would return a Promise from stepOne and resolve it after the console.log then the try catch will wait for stepOne and catch the error on stepTwo
Here is your code example
const stepOne = async () => {
return new Promise((resolve, reject) => {
setTimeout(function() {
console.log("step 1")
resolve(true);
}, 3000)
});
}
const stepTwo = async () => { throw new Error("Error at step two") }
const stepThree = async () => {
return new Promise((resolve, reject) => {
setTimeout(function() {
console.log("step 3")
resolve(true);
}, 3000)
});
}
(() => {
stepOne()
.then(stepTwo)
.then(stepThree)
.catch(error => {
console.log(error);
})
})();
Now the console.log looks like this
step 1
Error: Error at step two
at stepTwo (/home/user/develop/test/stackoverflow.js:10:38)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
Please try the below code. You need to wait for each call (stepOne, stepTwo and stepThree) so that when there is any exception, the next call is not made.
try {
await stepOne();
await stepTwo();
await stepThree()
} catch (error) {
console.log(error);
}
If your steps are functions that return a Promise, you could create a wrapper function that will call each step in sequence and abort if a step fails, logging details of which step failed.
In this example, each step fails with a 1 in 5 chance.
// Make a step proc, that throws 1 time in 5
function createStep(index) {
let error = (Math.random() < 0.2) ? new Error(`Step ${index+1} error`) : null ;
return () => new Promise((resolve, reject) => setTimeout(error ? reject(error): resolve(`Step ${index+1} outcome`), 500));
}
async function runSteps(steps) {
for(stepIndex = 0; stepIndex < steps.length; stepIndex++) {
try {
console.log(`Running step #${stepIndex+1}...`);
let result = await steps[stepIndex]();
console.log(`Step result:`, result);
} catch (e) {
console.error(`An error occurred at step #${stepIndex+1}:`, e.message);
break;
}
if (stepIndex === (steps.length -1) ) {
console.log("All steps completed successfully");
}
}
}
let steps = Array.from( { length: 3 }, (v,k) => createStep(k));
runSteps(steps);

Promise.all rollback successful promises' actions on failure

I am executing multiple promises with the following snippet:
await Promise.all([promise1, promise2, promise3]);
What I would like to achieve is to rollback the effects of the successful promises on the case of a failure from Promise.all().
In more specific terms, this means that the above will do some file encryptions, but if one fails, I would like to delete the other two (or one) files that were encrypted successfully so as to have consistent and clean file groups.
From what I've read this means that I would need two steps:
1. Catching the errors for each promise so that Promise.all() won't throw an error.
2. The puzzling part: Having another Promise.all() sort of:
await Promise.all([rollbackPromise1, rollbackPromise2, rollbackPromise3]);
This one seems to be the tricky part: Should I execute all the rollbacks independent of the promise that failed? This means that I should do another catch for every error such that the Promise.all() waits for every rollback to finish.
Is this the best way to do this, I find it pretty inefficient and ugly in terms of code.
You could create your own function implementing the asynchronous call of the functions and performing a rollback if required.
// Function that'll perform a promise.all and rollback if required
async function allWithRollback(promises) {
// using the map we are going to wrap the promise inside of a new one
return Promise.all(promises.map(([
func,
rollbackFunc,
], xi) => ((async() => {
try {
await func;
console.log('One Function succeed', xi);
} catch (err) {
console.log('One Function failed, require rollback', xi);
await rollbackFunc();
}
})())));
}
// Call the custom Promise.all
allWithRollback([
[
// First param is the promise
okPromise(),
// Second param is the rollback function to execute
() => {},
],
[okPromise(), () => {}],
[errPromise(), rollback1],
[errPromise(), rollback2],
[okPromise(), () => {}],
]);
// ---------
async function okPromise() {
return true;
}
async function errPromise() {
throw new Error('no one read this');
}
async function rollback1() {
console.log('Performed the rollback1');
}
async function rollback2() {
console.log('Performed the rollback2');
}
You can create a naive solution as follows:
const errorHandlers = []
function enc1 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('str')
}, 1000)
errorHandlers.push(() => {
console.log('handler 1')
})
})
}
function enc2 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('str')
}, 2000)
errorHandlers.push(() => {
console.log('handler 2')
})
})
}
function enc3 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('str')
}, 3000)
errorHandlers.push(() => {
console.log('handler 3')
})
})
}
Promise.all([enc1(), enc2(), enc3()]).then(() => {
console.log('all resovled')
}).catch((e) => {
errorHandlers.forEach(handler => handler(e))
})
It'd give you option to handle the 'global' error in each promise. Before creating promise all, you can reset the errorHandlers to prevent multiple errorHandler execution

Does promise resolved in n-th setTimeout cause memory leak?

I can see in Chrome task manager that the tab in which following code is running eats more and more memory, and it is not released until the promise is resolved
UPDATE
Main idea here is to use a single 'low level' method which would handle "busy" responses from the server. Other methods just pass url path with request data to it and awaiting for a valuable response.
Some anti-patterns was removed.
var counter = 1
// emulates post requests sent with ... axios
async function post (path, data) {
let response = (counter++ < 1000) ? { busy: true } : { balance: 3000 }
return Promise.resolve(response)
}
async function _call (path, data, resolve) {
let response = await post()
if (response.busy) {
setTimeout(() => {
_call(path, data, resolve)
}, 10)
throw new Error('busy')
}
resolve(response.balance)
}
async function makePayment (amount) {
return new Promise((resolve, reject) => {
_call('/payment/create', {amount}, resolve)
})
}
async function getBalance () {
return new Promise((resolve, reject) => {
_call('/balance', null, resolve)
})
}
makePayment(500)
.then(() => {
getBalance()
.then(balance => console.log('balance: ', balance))
.catch(e => console.error('some err: ', e))
})
The first time you call _call() in here:
async function getBalance () {
return new Promise((resolve, reject) => {
_call('/balance', null, resolve)
})
}
It will not call the resolve callback and it will return a rejected promise and thus the new Promise() you have in getBalance() will just do nothing initially. Remember, since _call is marked async, when you throw, that is caught and turned into a rejected promise.
When the timer fires, it will call resolve() and that will resolve the getBalance() promise, but it will not have a value and thus you don't get your balance. By the time you do eventually call resolve(response.balance), you've already called that resolve() function so the promise it belongs to is latched and won't change its value.
As others have said, there are all sorts of things wrong with this code (lots of anti-patterns). Here's a simplified version that works when I run it in node.js or in the snippet here in the answer:
function delay(t, val) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, val), t);
});
}
var counter = 1;
function post() {
console.log(`counter = ${counter}`);
// modified counter value to 100 for demo purposes here
return (counter++ < 100) ? { busy: true } : { balance: 3000 };
}
function getBalance () {
async function _call() {
let response = post();
if (response.busy) {
// delay, then chain next call
await delay(10);
return _call();
} else {
return response.balance;
}
}
// start the whole process
return _call();
}
getBalance()
.then(balance => console.log('balance: ', balance))
.catch(e => console.error('some err: ', e))

How to .catch a Promise.reject

I have a helper function for using fetch with CouchDB which ends as:
...
return fetch(...)
.then(resp => resp.ok ? resp.json() : Promise.reject(resp))
.then(json => json.error ? Promise.reject(json) : json)
and when I use it elsewhere, I was under the impression that I could .catch those explicit rejections:
above_function(its_options)
.then(do_something)
.catch(err => do_something_with_the_json_error_rejection_or_resp_not_ok_rejection_or_the_above(err))
but alas, I can't seem to be able to get a hold of the rejections.
The specific error I'm after is a HTTP 401 response.
What gives?
(Please note that there are implicit ES6 return's in the .thens)
function test() {
return new Promise((resolve, reject) => {
return reject('rejected')
})
}
test().then(function() {
//here when you resolve
})
.catch(function(rej) {
//here when you reject the promise
console.log(rej);
});
Make sure every call to a then() returns a value.
For e.g.
var url = 'https://www.google.co.in';
var options = {};
var resolves = Promise.resolve();
resolves.then(() => {
console.log('Resolved first promise');
var fetchPromise = fetch(url, options);
fetchPromise.then(() => {
console.log('Completed fetch');
});
})
.catch(error => {
console.log('Error', error);
});
Notice the console shows an uncaught exception. However, if you returned the inner promise (or any other value, which ends up turning into a promise via resolve), you end up flattening the promise so exception bubble up.
var url = 'https://www.google.co.in';
var options = {};
var resolves = Promise.resolve();
resolves.then(() => {
console.log('Resolved first promise');
var fetchPromise = fetch(url, options);
return fetchPromise.then(() => {
console.log('Completed fetch');
});
})
.catch(error => {
console.log('Error', error);
});
Notice the exception bubbles up to the outer promise. Hope this clears up things a little bit.
Why not wrap it in a try / catch block
// define a failing promise
const test = ()=> new Promise((resolve, reject) => reject('rejected'));
// using an immediately executing function to call an async block
(async ()=> {
try {
await test(); // => this will throw an error
} catch (er) {
console.log(er); // 'rejected'
}
})();
Promise rejections fall to the second param of the then function.
function test() {
return new Promise((resolve, reject) => {
return reject('rejected')
})
}
test().then(function() {
//here when you resolve
}, function(rej) {
//here when you reject the promise
console.log(rej)
})

Categories