var images = ['image', 'image2', 'image3'];
async function proccess_images(images) {
for (const image of images) {
await postprocess(image)
}
console.log('done'); //this prints out before for loop is finished
}
async function postprocess(image) {
await process(function(data) {
console.log(data);
}, image)
};
function process(callback, image) {
3rdpartylibrary.process(image, (err, data) => {
if(err) {
console.error(err);
} else {
console.log('not done')
callback(data);
}
})
}
proccess_images(images)
I'm having trouble getting an async/await function working with my code that does some image processing using a 3rd party library. I'm trying to get each image to finish processing before moving onto the next image, but currently all three start processing immediately without waiting. It seems like the 3rd party library is starting its process which makes the async/await function think its done, but how do I make sure the next image isn't processed until AFTER 3rdpartylibrary.process has finished processing the data? I've also tried putting
await new Promise ((resolve, reject) =>
In front of the 3rdpartylibrary function which stops them all from processing at the same time, but it causes the for loop to exit after processing the first image correctly. Any ideas as to what I'm doing wrong?
Your problem appears to be that your first function isn't returning a promise. await requires a promise to work correctly. Something like this should work:
async function postprocess(image) {
var output = await process(image);
console.log(output);
};
function process(image) {
return new Promise((resolve, reject) => {
3rdpartylibrary.process(image, (err, data) => {
if(err) {
console.error(err);
reject(err);
} else {
console.log('not done')
resolve(data);
}
})
});
}
Change the process function definition to wrap a Promise:
function process(image) {
return new Promise((resolve,reject) => {
3rdpartylibrary.process(image, (err, data) => {
if (err) reject(err);
resolve(data);
})
})
}
The key point being to remove any callback references, since now it returns a Promise so you can await it.
You want to wrap the 3rdpartylibrary in a promise. What you want to do is convert the callback style to a promise by wrapping it. This is a good article to refer to. Try this:
var images = ['image1', 'image2', 'image3'];
// mock thirdPartyLibrary
const thirdPartyLibrary = {
process: (image, callback) => {
console.log(`processing image ${image}`);
setTimeout(() => {
const noError = undefined;
callback(noError, `finished procesing image ${image}`);
}, 1000);
}
}
async function proccess_images(images) {
console.log('start');
for (const image of images) {
console.log(`before processing image ${image}`);
await process(image);
console.log(`after processing image ${image}`);
}
console.log('done');
}
function process(image) {
// wrap thirdPartyLibrary in promise
return new Promise((resolve, reject) => {
thirdPartyLibrary.process(image, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
})
})
}
proccess_images(images)
Related
I have the following snippet of code inside asynchronous function -
await application.save((err) => {
console.log("hello");
if (err) {
res.status(500).send({ message: "Error encountered" });
}
});
console.log("hey");
Why does "hey" get printed out earlier than "hello"? And how to fix it so the behaviour is as expected (asynchronous save operation is waited for and only when it's done and "hello" is printed, "hey" should be printed).
Following code does actually save object to MongoDB, but when I use application.save().then(() => {}) I get an error "Cannot read property 'then' of undefined"
You are confusing between callbacks and promises.
For example, we have first task F, long task T and next task N. We want to ensure the code run in the order F -> T -> N. To do that, we need either callbacks or promises.
Callbacks
function F() {}
function T(cb) {
// do that long task here
cb(err); // then call the cb and pass is the err
}
function N() {}
function main() {
F();
cb = (err) => {
if (err) { // handle err }
else {
N() // N needs to be run in cb
}
// clean up
// N can also be put here if you want to guarantee N will run
};
T(cb);
}
Promises
function F() {}
function T(cb) {
return new Promise((resolve, reject) => {
// do that long task here
if(err) {
reject();
} else {
resolve();
}
});
}
function N() {}
// using .then .catch .final
function main() {
F();
T().then(N())
.catch(() => {
// handle error
})
.finally(() => {
// clean up
// N can also be put here if you want to guarantee N will run
})
}
// using async/await
async function main() {
F();
try {
await T();
N();
} catch {
// handle error
} finally {
// clean up
// N can also be put here if you want to guarantee N will run
}
}
In your case, save() function does not return a promise but expects a callback to be passed in. If you want a quick and simple solution, put your console.log("hey"); in the callback. A better way is to promisify that callback function so it returns a promise and you can await it. Here is a sample promisify function to turn functions accepting callbacks into functions returning promises:
From
function T(...args, cb) {
// do that long task here
cb(err, data)
}
To
function TAsync(...args) {
return new Promise((resolve, reject) => {
T(...args, function(err, data) {
if (err) reject(err)
else resolve(data)
});
});
}
if a function returns a promise you just need to use the await and assign the return to a variable which is the response. You shoud always use a try catch because promises can launch exceptions.
function divide(i, j) {
return new Promise((resolve, reject) => {
if(j == 0) {
reject("division by zero.");
} else {
resolve(i / j);
}
});
}
async function main() {
try {
let resp = await divide(5, 2);
console.log(`resp = ${resp}`);
resp = await divide(2, 0);
console.log(`resp 2 = ${resp}`);
} catch(e) {
console.log(`catch = ${e}`);
}
}
// another way to use promises
divide(5,2).then((resp) => {
console.log(resp);
}).catch((err) => {
console.log(err);
});
divide(5,0).then((resp) => {
console.log(resp);
}).catch((err) => {
console.log(err);
});
main();
probably your function isn't returning a promise.
I'm trying to wait for a forEach to finish, and the forEach loop has two nested requests inside.
I need to wait untill the forEach finish beacuse I fill an array with the queries results and then, when the forEach is finish, then call another function, but I cannot do it well because sometimes, the array is fully filled, but othertimes the array is incomplete.
Here is my code:
readAllClientsAndInvoices: function(request, response) {
let clientsInvoices = [];
DAOClients.readAllClientesById(request.session.id, function (err, clients) {
if (err) {
console.log(err);
} else {
clients.forEach(function (client, idx, array) {
DAOClients.readClientDataById(client.id, function (err, data) {
if (err) {
console.log(err)
} else {
DAOClients.readAllclientInvoices(data.id, function (err, invoices) {
if (err) {
console.log(err);
} else {
let pair = {
clientData: data,
invoicesList: invoices
};
clientsInvoices.push(pair);
}
});
}
if (idx === array.length - 1) {
DAOClients.createClientPDFReportWOCommentsV2(clientsInvoices, function (err, res) {
if (err) {
console.log(err);
} else {
response.redirect(307, '/client/searchClient');
}
});
}
});
});
}
});
}
This is how I do it now, but I need to wait untill the array is fully filled with all the clients and its invoices and then call to createclientPDFReportWOCommentsV2 function but I don't know how to do it.
Thanks everyone
You can try to use a map instead of forEach in order to accept a return value from every call of the callback function, that return value will have to be a Promise, resolving after particular call has been completed. Since I don't see any particular error handling in your example I just made it so that in case of error Promise resolves undefined which is filtered afterwards in the createClientPDFReportWOCommentsV2 call.
function readAllClientsAndInvoices(request, response) {
DAOClients.readAllClientesById(request.session.id, function (err, clients) {
if (err) return console.log(err);
Promise.all(clients.map(client => {
return new Promise(resolve => {
DAOClients.readClientDataById(client.id, function (err, data) {
if (err) {
console.log(err)
resolve();
} else {
DAOClients.readAllclientInvoices(data.id, function (err, invoices) {
if (err) {
console.log(err);
resolve();
} else {
let pair = {
clientData: data,
invoicesList: invoices
};
resolve(pair);
}
});
}
});
});
})).then(clientsInvoices => {
DAOClients.createClientPDFReportWOCommentsV2(clientsInvoices.filter(Boolean), function (err, res) {
if (err) {
console.log(err);
} else {
response.redirect(307, '/client/searchClient');
}
});
});
});
}
To solve these problems i would use Async/Await https://javascript.info/async-await. Make sure all the methods you're calling on DAOClients returns a Promise https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
For example
function readAllClientesById() {
return new Promise((resolve, reject) => {
// Wait for some data to get fetched from SQL
// and call resolve instead of callback function
resolve(data)
// Or of there was an error
reject(err)
})
}
This is natively supported in the latest versions of Node.js.
Example of Async/Await if promises is implemented:
async function readAllClientsAndInvoices(req, res) {
try {
const clientInvoices = []
const clients = await DAOClients.readAllClientesById(req.session.id)
for (const client of clients) {
const clientData = await DAOClients.readClientDataById(client.id)
const clientInvoices = await DAOClients.readAllclientInvoices(clientData.id)
clientInvoices.push({
clientData: data,
invoicesList: invoices
})
}
// This code won't be executed until the for loop is completed
await DAOClients.createClientPDFReportWOCommentsV2(clientInvoices)
} catch (err) {
return res.status(err.code).send(err)
}
res.redirect(307, '/client/searchClient');
}
I haven't tested the code, it's just an example of how I approach these type of problems.
I have 3 functions which I need to run in order and one needs to finish before the other runs so I've done this:
var fs = require('fs');
async function create() {
fs.writeFile('newfile.txt', 'some text here', (err) => {
if (err) throw err;
console.log('File is created successfully.');
return ('File is created successfully.');
});
}
async function waitt() {
setTimeout(() => { return 'waited!' }, 10000);
}
async function read() {
fs.readFile('newfile.txt', {encoding: 'utf-8'}, (err,data) => {
if (!err) {
console.log('received data: ' + data);
return ('received data: ' + data);
} else {
console.log(err);
}
});
}
async function process() {
let [r1, r2, r3] = await Promise.all([create(), waitt(), read()]);
console.log(r1 + ' ' + r2 + ' ' + r3);
}
process();
So, process() runs create() which creates a file, then run waitt() which just pauses and finally read() shows the contents of the file.
The issue I'm having is that it's running in this order:
create()
read()
and then waitt()
instead of
create()
waitt()
read()
which is what I want.
How can I fix this?
This won't work:
async function waitt() {
setTimeout(() => { return 'waited!' }, 10000);
}
Because, you're return-ing from within the setTimeout callback, not the async-marked wait function.
To mix callback-style code and async/await you must first convert callback style code to use Promises, or use fs-extra which already provides fs functions returning promises.
Something like this:
function waitt() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('waited...')
}, 10000)
})
}
The same applies to the rest of your functions.
Also note that if a function explicitly returns a Promise, it doesn't need to be marked as async to be awaited, since await essentially works with Promises.
Now for the order:
Promise.all doesn't guarantee order, for that you might be able to get away with a simple for..of, or just call the functions yourself.
function wait() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('waited...')
resolve('foo')
}, 500)
})
}
// Assume this is your promisified read function.
function read() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('read...')
resolve('bar')
}, 500)
})
}
// Assume this is your promisified create function.
function create() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('created...')
resolve('baz')
}, 500)
})
}
;(async () => {
try {
await create()
await wait()
await read()
} catch (err) {
console.error(err)
}
})()
Your problem is that Promise.all does not guarantee the order of processing, just that all the promises in the list are processed.
Can you not just say:
await create();
await read();
await waitt();
I have the following async function:
async function readFile () {
let content = await new Promise((resolve, reject) => {
fs.readFile('./file.txt', function (err, content) {
if (err) {
return reject(err)
}
resolve(content)
})
})
console.log(content)
}
readFile()
This runs just fine. It outputs the file buffer to the console as expected. But now, if I try to instead return the value:
async function readFile () {
let content = await new Promise((resolve, reject) => {
fs.readFile('./file.txt', function (err, content) {
if (err) {
return reject(err)
}
resolve(content)
})
})
return content
}
console.log(readFile())
I now get:
Promise { <pending> }
Why is this? Why can you use a value inside that function but when you return it out of the function it's now a Promise?
How do you actually make use of this in a normal workflow? For example, lets say I wanted to check if a file exists, then read in the file, then update some database with the content, the synchronous pseudo code would look something like this:
if (fileExists(path)) {
buffer = readFile(path)
updateDatabase(buffer)
}
That workflow consists of 3 individual async operations. How would you do something like this with async/await? Is the key that you have to have your entire script wrapped in an async function?
async function doSomething () {
if (fileExists(path)) {
buffer = readFile(path)
updateDatabase(buffer)
}
}
(Keep in mind that is just pseudo-code but hopefully its gets my point across).
All async functions return a promise as was mentioned in the comments. You could therefore re-write your readFile function like this:
function readFile() {
return new Promise((resolve, reject) => {
fs.readFile('./file.txt', function (err, content) {
if (err) {
return reject(err)
}
resolve(content)
})
})
}
You would then consume the return value of readFile via await:
console.log(await readFile()) // will log your actual file contents.
The usual workflow with this paradigm is to break your async operations into separate functions that each return a promise, and then run them all inside a broader async function, much like you suggest, but with awaits and some error handling like so:
async function doSomething () {
try {
const fileCheck = await fileExists(path)
if (fileCheck) {
const buffer = await readFile(path)
await updateDatabase(buffer)
// Whatever else you want to do
}
} catch (err) {
// handle any rejected Promises here.
}
}
const serchContentXmlFile = async (path, content) => {
return new Promise((resolve, reject) => {
fs.readFile(path, function (err, data) {
if (err) {
return reject(err)
}
resolve(data.indexOf(content))
})
})
}
await serchContentXmlFile("category.xml",xmlUrl);
here are two method .but i need it to finish sync.But it is failed.
I want to use the Promise.all to keep it sync but i find the images process is async and it can't reject or resolve in a loop! It really makes me confused .
below is my code.I can't catch the res or err. it just end after processed the images...even don't excute the test Method,but i wonder that Promise.all is a sync method? And is there any way to catch the res or err information? Thanks !
var gm = require('gm')
var fs = require('fs')
var config = require('./imgConfig')
var image = ['1.jpg', '2.jpg', '3.jpg', '4.jpg','5.jpg','6.jpg']
var _image = ['_1.jpg', '_2.jpg', '_3.jpg', '_4.jpg','_5.jpg','6.jpg']
testS()
function testS() {
uploadFiles(image, _image, 'subModule').then(res => {
console.log(res)
}).catch(e => {
console.log(e)
})
}
function uploadFiles(inputArray, outputArray, type) {
return Promise.all([multipleResize(inputArray, outputArray, type), test()])
}
function test() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("object");
resolve("yeah")
}, 100)
})
}
function multipleResize(inputArray, outputArray, type) {
var height = config[type].height;
var width = config[type].width;
return Promise.all(inputArray.map((img, index) => {
return new Promise((resolve, reject) => {
gm(config.inputPath + img)
.resizeExact(width, height)
.noProfile()
.write(config.outputPath + outputArray[index], function (err) {
if (err) {
console.error(err);
return reject(err);
}
try {
fs.unlinkSync(config.inputPath + img); // consider using an async method here as well.
console.log("next" + config.outputPath + outputArray[index]);
console.log('ko');
return resolve();
} catch (e) {
console.error(e);
reject(e);
}
});
});
}));
}
imgConfig
module.exports = {
'subModule': {
width: 300,
height: 300
},
inputPath:'./input/',
outputPath:'./output/'
}
In multipleResolve, you are running return resolve() as part of the #forEach() call. This probably isn't what you want, as the first iteration will resolve the promise you returned, even though all of your gm .write() calls won't be finished yet.
I would consider doing something like this -- note the internal use of Promise.all() and the inputArray#map() to convert each img/index pair to their own Promise. I also swapped around some of the error logic to make sure the errors are checked as soon as possible.
function multipleResize(inputArray, outputArray, type) {
var height = config[type].height;
var width = config[type].width;
var flag = 0;
return Promise.all(inputArray.map((img, index) => {
return new Promise((resolve, reject) => {
gm(config.inputPath + img)
.resizeExact(width, height)
.noProfile()
.write(config.outputPath + outputArray[index], function (err) {
if (err) {
console.error(err);
return reject(err);
}
try {
fs.unlinkSync(config.inputPath + img); // consider using an async method here as well.
console.log("next" + config.outputPath + outputArray[index]);
flag++;
console.log('ko');
return resolve();
} catch (e) {
console.error(e);
reject(e);
}
});
});
}));
}
Side note, you seem to be using promises (mostly) correctly, but you are incorrectly calling them sync, when they typically aren't synchronous.