Reputation: 179
I have an express app and want to return response only after the file is downloaded and decompressed. My code looks like this:
function downloadObject() {
return getObject(...).then((archive) => {
console.log("Downloaded.");
fs.writeFileSync(...);
return decompress(..., function(err) {
if (err) {
console.log("Decompression error.");
} else {
console.log("Decompressed");
}
});
})
}
app.post("/download", async function (req, res) {
await downloadObject();
res.send('ready');
});
It does wait until the file is downloaded, however, it returns response before the file is decompressed. How can I make it wait for the inner promise to be resolved too?
Upvotes: 0
Views: 131
Reputation: 5534
You are actually returning the result of the decompress
function, but it's an async function, so the return value doesn't really matter and is probably undefined
.
What matters is the callback that you pass in, whose signature probably looks like the following :
decompress(..., function(err, result) {
// Do what you want with the error and the result
})
To use promise instead of callback to achieve your asynchronous task, you can wrap your function in a promise like the following, and since your inside the then
of another Promise, it will chain.
function downloadObject() {
return getObject(...).then((archive) => {
console.log("Downloaded.");
fs.writeFileSync(...);
return new Promise((resolve, reject) =>
decompress(..., function(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
})
);
})
}
That's not all, it works, but we can do better. writeFileSync
will block your IO
in Node and we don't want that. Instead, it is preferable to use writeFile
and everything would be cleaner using Promise chaining :
function downloadObject() {
return getObject(...)
.then((archive) => new Promise((resolve, reject) => {
fs.writeFile("<filename>", archive?.data, '<file-encoding>', err => {
if (err) {
reject(err);
} else {
resolve(archive);
}
})
}))
.then((archive) => new Promise((resolve, reject) => {
decompress(archive, function(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
})
}));
}
Of course, it's pseudo-code, I don't know what is in your archive
object, nor do I know the type signature of your decompress
function, but the point is to show how to wrap callback-style function in Promises, chain them and also handle errors properly with the reject
function provided by the Promises.
Upvotes: 2
Reputation: 30807
Without knowing too much about decompress
, I did notice it takes a callback.
That means it is itself an asynchronous function and immediately returns.
Use util.promisify
to turn it into a regular promise-producing function:
async function downloadObject() {
const archive = await getObject(...);
console.log("Downloaded.");
fs.writeFileSync(...); // Or `util.promisify(fs.writeFile)`
try {
await util.promisify(decompress)(...);
console.log("Decompressed");
} catch (err) {
console.log("Decompression error.");
}
}
This function implements a common pattern that was mentioned in a now-deleted answer:
util.promisify = function (f) {
return function(...args) {
return new Promise((resolve, reject) => {
f(...args, (err, result) => { err ? reject(err) : resolve(result); });
})
}
}
Upvotes: 1