How to flatten a Promise within a Promise? - javascript

I have the following 2 functions, each returns a Promise:
const getToken = () => {
return new Promise((resolve, reject) => {
fs.readFile('token.txt', (err, data) => {
if (err) { return reject(err) }
if (!tokenIsExpired(data.token)) {
return resolve(data.token)
} else {
return requestNewToken()
}
})
})
}
const requestNewToken = () => {
return new Promise((resolve, reject) => {
restClient.get(url, (data, res) => {
fs.writeFile('tokenfile.txt', data.token, err => {
resolve(data.token)
})
})
})
}
function1()
.then(value => {
console.log('taco')
})
.catch(err => {
console.log(err)
})
So function1 runs, and (depending on some condition), it sometimes returns function2, which is returning another Promise. In this code, when function2 is called, the console.log('taco') never runs. Why is this? I thought that if you return a Promise from within a Promise, the resolved value of the nested Promise is what is resolved at the top level.
In order for me to get this to work, I have to do this:
const getToken = () => {
return new Promise((resolve, reject) => {
if (!tokenIsExpired()) {
return resolve(getToken())
} else {
return requestNewToken ()
.then(value => {
resolve(value)
})
}
})
}
That works, but it seems like I'm doing something wrong. It seems like there should be a more elegant way to handle/structure this.

You're right that promises auto-unwrap, but in this case you're returning from inside a promise constructor, which is ignored, you need to invoke either resolve or reject instead of using return. I think this might be the solution you're looking for:
const function1 = () => {
return new Promise((resolve, reject) => {
if (someCondition) {
resolve('foobar')
} else {
resolve(function2());
}
})
}
Inside a promise constructor, you need to call resolve or reject, which are equivalent to using return or throw from inside a then callback.
If you find this distinction confusing (I do), you should avoid the promise constructor entirely by just beginning a new chain with Promise.resolve, like this:
const function1 = () => {
return Promise.resolve().then(() => {
if (someCondition) {
return 'foobar';
} else {
return function2();
}
})
}
const function2 = () => {
return new Promise((resolve, reject) => {
resolve('hello world')
})
}
someCondition = false;
function1()
.then(value => {
console.log(value)
})
With your updated code, I recommend using a library to wrap APIs, rather than accessing the promise constructor yourself. For example, using bluebird's promisify:
const bluebird = require('bluebird');
const readFile = bluebird.promisify(fs.readFile);
const writeFile = bluebird.promisify(fs.writeFile);
const getUrl = bluebird.promisify(restClient.get, {multiArgs:true});
const getToken = () => {
return readFile('token.txt')
.then((data) => {
if(!tokenIsExpired(data.token)) {
return data.token;
} else {
return requestNewToken();
}
});
};
const requestNewToken = () => {
return getUrl(url)
.then(([data, res]) => {
return writeFile('tokenFile.txt', data.token)
.then(() => data.token);
});
};
I've remained faithful to your source code, but I'll note there may be a bug to do with writing data.token, and later trying to read the token property in that file.
Why use a library instead of the Promise constructor?
It allows you to write code which deals only with promises, which is (hopefully) easier to understand
You can be confident that callback APIs are correctly converted without losing errors. For example, if your tokenIsExpired function throws an error, using your promise constructor code, it would be lost. You would need to wrap all of your inner callback code in try {} catch(e) {reject(e)}, which is a hassle and easily forgotten.

Related

"asyncing" new Promise( resolve, reject ) [duplicate]

I'm using the async.eachLimit function to control the maximum number of operations at a time.
const { eachLimit } = require("async");
function myFunction() {
return new Promise(async (resolve, reject) => {
eachLimit((await getAsyncArray), 500, (item, callback) => {
// do other things that use native promises.
}, (error) => {
if (error) return reject(error);
// resolve here passing the next value.
});
});
}
As you can see, I can't declare the myFunction function as async because I don't have access to the value inside the second callback of the eachLimit function.
You're effectively using promises inside the promise constructor executor function, so this the Promise constructor anti-pattern.
Your code is a good example of the main risk: not propagating all errors safely. Read why there.
In addition, the use of async/await can make the same traps even more surprising. Compare:
let p = new Promise(resolve => {
""(); // TypeError
resolve();
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e)); // Catches it.
with a naive (wrong) async equivalent:
let p = new Promise(async resolve => {
""(); // TypeError
resolve();
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e)); // Doesn't catch it!
Look in your browser's web console for the last one.
The first one works because any immediate exception in a Promise constructor executor function conveniently rejects the newly constructed promise (but inside any .then you're on your own).
The second one doesn't work because any immediate exception in an async function rejects the implicit promise returned by the async function itself.
Since the return value of a promise constructor executor function is unused, that's bad news!
Your code
There's no reason you can't define myFunction as async:
async function myFunction() {
let array = await getAsyncArray();
return new Promise((resolve, reject) => {
eachLimit(array, 500, (item, callback) => {
// do other things that use native promises.
}, error => {
if (error) return reject(error);
// resolve here passing the next value.
});
});
}
Though why use outdated concurrency control libraries when you have await?
I agree with the answers given above and still, sometimes it's neater to have async inside your promise, especially if you want to chain several operations returning promises and avoid the then().then() hell. I would consider using something like this in that situation:
const operation1 = Promise.resolve(5)
const operation2 = Promise.resolve(15)
const publishResult = () => Promise.reject(`Can't publish`)
let p = new Promise((resolve, reject) => {
(async () => {
try {
const op1 = await operation1;
const op2 = await operation2;
if (op2 == null) {
throw new Error('Validation error');
}
const res = op1 + op2;
const result = await publishResult(res);
resolve(result)
} catch (err) {
reject(err)
}
})()
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e));
The function passed to Promise constructor is not async, so linters don't show errors.
All of the async functions can be called in sequential order using await.
Custom errors can be added to validate the results of async operations
The error is caught nicely eventually.
A drawback though is that you have to remember putting try/catch and attaching it to reject.
BELIEVING IN ANTI-PATTERNS IS AN ANTI-PATTERN
Throws within an async promise callback can easily be caught.
(async () => {
try {
await new Promise (async (FULFILL, BREAK) => {
try {
throw null;
}
catch (BALL) {
BREAK (BALL);
}
});
}
catch (BALL) {
console.log ("(A) BALL CAUGHT", BALL);
throw BALL;
}
}) ().
catch (BALL => {
console.log ("(B) BALL CAUGHT", BALL);
});
or even more simply,
(async () => {
await new Promise (async (FULFILL, BREAK) => {
try {
throw null;
}
catch (BALL) {
BREAK (BALL);
}
});
}) ().
catch (BALL => {
console.log ("(B) BALL CAUGHT", BALL);
});
I didn't realized it directly by reading the other answers, but what is important is to evaluate your async function to turn it into a Promise.
So if you define your async function using something like:
let f = async () => {
// ... You can use await, try/catch, throw syntax here (see answer of Vladyslav Zavalykhatko) ..
};
your turn it into a promise using:
let myPromise = f()
You can then manipulate is as a Promise, using for instance Promise.all([myPromise])...
Of course, you can turn it into a one liner using:
(async () => { code with await })()
static getPosts(){
return new Promise( (resolve, reject) =>{
try {
const res = axios.get(url);
const data = res.data;
resolve(
data.map(post => ({
...post,
createdAt: new Date(post.createdAt)
}))
)
} catch (err) {
reject(err);
}
})
}
remove await and async will solve this issue. because you have applied Promise object, that's enough.

chaining promises in functions

I have a small problem, how to create a promise chain in a sensible way so that the makeZip function will first add all the necessary files, then create the zip, and finally delete the previously added files? (The makeZip function also has to return a promise). In the example below I don't call deleteFile anywhere because I don't know exactly where to call it. when I tried to call it inside the add file function to delete the file immediately after adding it, for some unknown reason the console displayed the zip maked! log first and then file deleted.
const deleteFile = (file, result) => {
new Promise((resolve, reject) => {
fs.unlink(`./screenshots/${file}`, (err) => {
if (err) return reject(err);
console.log(`${file} deleted!`);
return resolve();
});
});
};
const addFile = (file) => {
new Promise((resolve, reject) => {
try {
zip.addLocalFile(`./screenshots/${file}`);
console.log(`${file} added`);
return resolve();
} catch {
return reject(new Error("failed to add file"));
}
});
};
const makeZip = () => {
Promise.all(fs.readdirSync("./screenshots").map((file) => addFile(file)))
.then(() => {
return new Promise((resolve, reject) => {
try {
zip.writeZip(`./zip_files/supername.zip`);
console.log("zip maked!");
resolve();
} catch {
return reject(new Error("failed making zip"));
}
});
})
.catch((err) => console.log(err));
};
the main cause of this is that you are not returning the promises you are instantiating in your function calls. Also I have some cool suggestion to make that can improve you code cleanliness.
[TIP]: Ever checked the promisify function in NodeJS util package, it comes with node and it is very convenient for converting functions that require callbacks as arguments into promise returning functions., I will demonstrate below anyhow.
// so I will work with one function because the problem resonates with the rest, so
// let us look at the add file function.
// so let us get the promisify function first
const promisify = require('util').promisify;
const addFile = (file) => {
// if addLocalFile is async then you can just return it
return zip.addLocalFile(`./screenshots/${file}`);
};
// okay so here is the promisify example, realized it wasn't applicable int the function
// above
const deleteFile = (file, result) => {
// so we will return here a. So because the function fs.unlink, takes a second arg that
// is a callback we can use promisify to convert the function into a promise
// returning function.
return promisify(fs.unlink)(`./screenshots/${file}`);
// so from there you can do your error handling.
};
So now let us put it all together in your last function, that is, makeZip
const makeZip = () => {
// good call on this, very interesting.
Promise.all(fs.readdirSync("./screenshots").map((file) => addFile(file)))
.then(() => {
return zip.writeZip(`./zip_files/supername.zip`);
})
.then(() => {
//... in here you can then unlink your files.
});
.catch((err) => console.log(err));
};
Everything should be good with these suggestions, hope it works out...
Thank you all for the hints, the solution turned out to be much simpler, just use the fs.unlinkSync method instead of the asynchronous fs.unlink.
const deleteFile = (file) => {
try {
fs.unlinkSync(`./screenshots/${file}`);
console.log(`${file} removed`);
} catch (err) {
console.error(err);
}
};
const addFile = (file) => {
try {
zip.addLocalFile(`./screenshots/${file}`);
console.log(`${file} added`);
deleteFile(file);
} catch (err) {
console.error(err);
}
};
const makeZip = () => {
fs.readdirSync("./screenshots").map((file) => addFile(file));
zip.writeZip(`./zip_files/supername.zip`);
console.log("zip maked!");
};

How do I access promise callback value outside of the function?

It is to my understanding that callback functions are asynchronous and cannot return a value like regular functions. Upon reading about promises, I thought I grasped a good understanding about them, that they are basically an enhanced version of callbacks that allows returning a value like asynchronous function. In my getConnections method, I am attempting to call the find() function on my database through mongoose, and I am attempting to grab this array of objects and send it to the views.
var test = new Promise((resolve, reject) => {
Database.find().then(list => {
resolve(list);
}).catch(err=> {
return reject(err);
})
})
console.log(test)
When I attempt to log to the console outside of the promise function, I get Promise { _U: 0, _V: 0, _W: null, _X: null }
I don't think this is functioning correctly, and I thought I utilized promises correctly. Could anyone point me in the right direction on how to return this array of objects outside of the callback function?
You can simply add await before the promise declaration.
i.e.
var test = await new Promise...
The thing is that when you write a function like this:
const getData = async () => { const response = await fetch(someUrl, {}, {}); return response;}
Then you also need to await that function when you call it. Like this:
const setData = async () => { const dataToSet = await getData(); }
let test = new Promise((resolve, reject) => {
Database.find().then(list => {
resolve(list);
}).catch(err=> {
return reject(err);
})
})
test
.then(result=>console.log(result))
Should solve your problem.
var someValue;
var test = await new Promise((resolve, reject) => {
Database.find().then(list => {
resolve(list);
}).catch(err=> {
return reject(err);
})
}).then(res => {
someValue=res;
})
console.log(someValue);

How to define a promise chain without using then method

I already looked for similar questions, but they are related to JQuery or any other library.
First, I wrote this:
const printIn1Sec = (value) => {
return new Promise(resolve => {
setTimeout(() => {
console.log(value);
resolve();
}, 1000)
});
};
And used it in this way:
printIn1Sec(1)
.then(() => printIn1Sec(2))
.then(() => printIn1Sec(3));
I think then is very important, because it allows us to execute something as soon as the promise is resolved.
But I was looking for something like this:
printIn1Sec(1)
.printIn1Sec(2)
.printIn1Sec(3);
I noticed I needed an object with access to this printIn1Sec method. So I defined a class:
class Printer extends Promise {
in1Sec(v) {
return this.then(() => this.getPromise(v));
}
getPromise(value) {
return new Printer(resolve => {
setTimeout(() => {
console.log(value);
resolve();
}, 1000)
})
}
}
And used it this way:
Printer.resolve().in1Sec(1).in1Sec(2).in1Sec(3);
I had to resolve the Promise from the beginning, in order to the start the chain. But it still bothers me.
Do you think, is there a way to get it working like the following?
printIn1Sec(1).printIn1Sec(2).printIn1Sec(3);
I was thinking in a new class or method, that could receive these values, store them, and finally start resolving the chain.
But it would require to call an aditional method at the end, to init with the flow.
If you really wanted to create a chainable interface as in your question, this would do it:
const printIn1Sec = (function() {
function setTimeoutPromise(timeout) {
return new Promise(resolve => setTimeout(resolve, 1000));
}
function printIn1Sec(value, promise) {
const newPromise = promise
.then(() => setTimeoutPromise(1000))
.then(() => console.log(value));
return {
printIn1Sec(value) {
return printIn1Sec(value, newPromise);
},
};
}
return value => printIn1Sec(value, Promise.resolve());
}());
printIn1Sec(1)
.printIn1Sec(2)
.printIn1Sec(3);
We just hide all the promise creation and chaining in an internal function. I split the code into smaller functions to make it a bit nicer looking.
You can try async and await
const printIn1Sec = (value) => {
return new Promise(resolve => {
setTimeout(() => {
console.log(value);
resolve();
}, 1000)
});
};
async function fun(){
await printIn1Sec(1);
await printIn1Sec(2);
await printIn1Sec(3);
}
fun();

How to handle an array of Promises then/catch

I don't know to to resolve this situation using JS promises.
Imagine I have some articles and I want to send a patch request to update them. I send one request per article. If the request of one article success then I update that article, but if the request fails, then I update the article differently. Also I want to show a message to the user informing if all the articles have been updated correctly or not.
This is not my real scenario and this may be a weird example. But it's what I want to accomplish in my React app.
Here is what I'm trying to do right now:
const saveArticles = articles => {
const promises = [];
articles.forEach(article => {
const promise = axios
.patch('/articles', article)
.then(() => updateArticleUi(article))
.catch(() => updateArticleUiWithError(article));
promises.push(promise);
});
Promise.all(promises)
.then(() => tellTheUserThereWasNoErrors())
.catch(() => tellTheUserThereWasSomeErrors());
};
This isn't working because the Promise.all is always executing the then callback, weather all promises succeed or not.
Thanks!
Your updateArticleUi and updateArticleUiWithErrors should have the respective return values so that you can distinguish whether there was an error or not when looking at the array of results:
function updateArticleUi(article) {
…
return {success: true};
}
function updateArticleUiWithError(article, error) {
…
return {success: false};
}
function saveArticles(articles) {
const promises = articles.map(article => {
return axios.patch('/articles', article).then(() =>
updateArticleUi(article)
, err =>
updateArticleUiWithError(article, err)
);
});
return Promise.all(promises).then(results =>
if (results.every(res => res.success)) {
tellTheUserThereWasNoErrors();
} else {
tellTheUserThereWasSomeErrors();
}
});
}
This works:
function axiosPatchRequest(url,data) {
return new Promise(function (resolve, reject) {
if (data) {
resolve(url);
} else {
reject('DATA_NOT_FOUND');
}
})
}
function updateArticleUi(data,article) {
return new Promise(function (resolve, reject) {
resolve({type:"updateArticleUi ",data,article});
})
}
function updateArticleUiWithError(data,article) {
return new Promise(function (resolve, reject) {
reject({type:"updateArticleUiWithError ",data,article});
})
}
function tellTheUserThereWasNoErrors(data){
console.log("tellTheUserThereWasNoErrors",data);
}
function tellTheUserThereWasSomeErrors(error){
console.log("tellTheUserThereWasSomeErrors",error);
}
const execute = (articles)=>{
const promises = [];
articles.forEach(article => {
const promise = axiosPatchRequest('/articles', article)
.then((data) => {
return updateArticleUi(data,article);
})
.catch((error) => {
return updateArticleUiWithError(error,article);
});
promises.push(promise);
});
Promise.all(promises)
.then((data) => tellTheUserThereWasNoErrors(data))
.catch((error) => tellTheUserThereWasSomeErrors(error));
};
execute(["one","","three"]);
execute(["one","two","three"]);
Promise.all will always call then because the promise you add to promises is already resolved one with either then or catch attached above
if you want to see catch to happen in .all chain just throw some exeption in updateArticleUiWithErro function
The problem is that you are catching the error thrown by
const promise = axios
.patch('/articles', article)
.then(() => updateArticleUi(article))
.catch(() => updateArticleUiWithError(article));
you should either throw a new error in the axios catch() or let the error bubble up by removing the catch from the axios call.

Categories

Resources