I read this article from node
https://nodejs.org/en/docs/guides/blocking-vs-non-blocking/
It says the code below is a process blocker:
const fs = require('fs');
const data = fs.readFileSync('/file.md'); // blocks here until file is read
console.log(data);
// moreWork(); will run after console.log
what if I add await?
will the code above becomes non-blocking or it will stay in its true nature?
Example code:
const fs = require('fs');
const data = await fs.readFileSync('/file.md'); // no more blocking
console.log(data);
Thank you
No, the code can't run since await must use in async function.
And await should use for function that return promise.
the code means:
// await new Promise(...)
// console.log(...)
new Promise().then((...) => console.log(...))
If you should non-block function, you should use fs.readFile instead.
Blocking means that the whole application is blocked.
So, all SetInterval, Promise, Events or whatever async callback are paused until that sync function ends its execution.
It's the same of what you get when you use a for..loop.
NodeJS provides you some non-blocking file system methods with callbacks, just change your code like this.
const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
if (err) throw err;
console.log(data)
});
There is the only way.
await operator wait a promise, and wrapped in async function
you should code like this
const fs = require("fs");
function readFile(fileName) {
return new Promise((resolve, reject) => {
fs.readFile(fileName, (err, data) => {
if (err) reject(err);
resolve(data);
});
});
}
async function f1() {
try {
var x = await readFile("foo.json");
console.log(x);
} catch (e) {
console.log(e); // 30
}
}
f1();
Related
I want to receive an image from a client, convert it into text and then delete the image once it's converted. I'm using the following code:
app.post('/upload', (req,res)=>{
const myFile = req.files.file;
myFile.mv(`D:/web_projects/react-express-mongodb-template/server/pictures/${myFile.name}`)
let img = `D:/web_projects/react-express-mongodb-template/server/pictures/${myFile.name}`
convert(img);
remove(img)
})
app.listen(5000, () => {
console.log('server is running at port 5000');
})
async function convert(img){
const worker = createWorker();
console.log(worker)
await worker.load();
await worker.loadLanguage('eng');
await worker.initialize('eng');
const { data: { text } } = await worker.recognize(img);
console.log(text);
await worker.terminate();
}
async function remove(path){
try {
fs.unlink(path)
} catch(err) {
console.error(err)
}
}
So in the post method I call the convert function and then remove but remove gets executed first and so convert function results in error. Is there a way to handle this issue?
As convert is an async function, and thus returns a promise, replace:
convert(img);
remove(img)
With:
convert(img).then(() => remove(img));
However, this assumes that all awaited worker method calls in convert return promises. If this is not the case, and they run asynchronous code, then actually convert will not properly await those, and you should first promisify them.
I feel like I a missing something fundamental here. I simply want to ensure "data" holds the parsed data from parseCSV before I continue to clean/edit it.
What am I missing? I fundamentally understand async / await but I suppose there is something I don't understand when it comes to callbacks and async / await.
const csv = require("csv");
const fs = require("fs");
(async function start() {
const file = importCSV("src/tg.csv");
const data = await parseCSV(file);
console.log(await parseCSV(file)); // why does this print a parser and not the parsed data?
})();
function importCSV(path) {
return fs.readFileSync(path, "utf8");
}
async function parseCSV(file) {
return await csv.parse(file, { columns: true }, async (err, data) => {
if (err) return err;
//console.log(data);
return data;
});
}
Because you are not returning the data. the csv.parse is not a promise so basically the data is being returned to the parent function of the anonymous callback function.
Just think of the implementation of a function who uses callback, how do you implement that?
const a = (cb) => {
const data = getdatafromsomewhere();
cb(data);
}
so if your function is not specifically returning to the parent function the returned data is not referenced from anywhere.
So the easiest way would be to promisify the csv.parse
const util = require('util');
const promiseParse = util. promisify(csv.parse);
function parseCSV(file) {
return promiseParse(file, { columns: true })
}
I am interested in understanding how to Promisfy this block of code:
const http = require('http');
const fs = require('fs');
const download = function(url, dest, cb) {
let file = fs.createWriteStream(dest);
const request = http.get(url, function(response) {
response.pipe(file);
file.on('finish', function() {
file.close(cb); // close() is async, call cb after close completes.
});
}).on('error', function(err) { // Handle errors
fs.unlink(dest); // Delete the file async. (But we don't check the result)
if (cb) cb(err.message);
});
};
My first take on this was something to the extent of:
const http = require('http');
const fs = require('fs');
const download = async (url, dest, cb) => {
let file = fs.createWriteStream(dest);
const request = http.get(url, function(response) {
response.pipe(file);
file.on('finish', function() {
const closed = await file.close(cb); // close() is async, await here?
if (closed) {
// handle cleanup and retval
}
});
}).on('error', function(err) { // Handle errors
const deleted = await fs.unlink(dest); // Delete the file async.
if (!deleted) { ... }
});
};
The implementation above is clearly wrong. What is the right away to approach this to remove callbacks and just use async/await?
Here's a way to manually wrap the pipe operation in a promise. Unfortunately, most of this is just error handling to cover all the possible places an error can occur:
const http = require('http');
const fs = require('fs');
const download = function(url, dest) {
return new Promise((resolve, reject) => {
const file = fs.createWriteStream(dest);
// centralize error cleanup function
function cleanup(err) {
reject(err);
// cleanup partial results when aborting with an error
file.on('close', () => {
fs.unlink(dest);
});
file.end();
}
file.on('error', cleanup).on('finish', resolve);
const request = http.get(url, function(response) {
if (response.status < 200 || response.status >= 300) {
cleanup(new Error(`Unexpected Request Status Code: ${response.status}`);
return;
}
response.pipe(file);
response.on('error', cleanup);
}).on('error', cleanup);
});
};
download(someURL, someDest).then(() => {
console.log("operation complete");
}).catch(err => {
console.log(err);
});
This does not wait for files to be closed or removed in the error conditions before rejecting (figuring that there's typically nothing constructive to do if those cleanup operations have errors anyway). If that is desired, it could be added easily by just calling reject(err) from the asynchronous callbacks for those cleanup operations or by using the fs.promises version of those functions and awaiting them.
A few things to note. This is mostly error handling because there are three possible places you can have errors and some cleanup work needed for some errors.
Added required error handling.
In the OP's original code, they called file.close(), but file is a stream and there is no .close() method on a writeStream. You call .end() to close the write stream.
You also probably need to check for an appropriate response.status because http.get() still returns a response object and stream even if the status is something like 4xx or 5xx.
Here's how I'd re-write your node-style callback API as an asynchronous function:
const http = require('http');
const fs = require('fs');
async function download (url, dest) {
const response = await new Promise((resolve, reject) => {
http.get(url, resolve).once('error', reject);
});
if (response.status < 200 || response.status >= 300) {
throw new Error(`${responses.status} ${http.STATUS_CODES[response.status]}`);
}
const file = await fs.promises.open(dest, 'w');
try {
for await (const data of response) {
await file.write(data);
}
} catch (error) {
await file.close();
await fs.promises.unlink(dest);
throw error;
}
await file.close();
}
Note that this approach uses the FileHandle class in the fs.promises namespace, as well as the Symbol.asyncIterator interface defined on the Readable stream class, which allows you to consume the data events of the response with a for await...of loop and propagate error handling from the error event of the response to the catch block by implicitly rejecting the promise returned by the underlying asynchronous iterator.
I'm trying to test a simple function that generates a random name using the nodejs crypto library. I'm using sinon to stub out a method call within the callback of pseudoRandomBytes but the stub doesn't seem to be called. Example:
getFileName.js
const crypto = require('crypto');
module.exports = (req, file, cb) => {
crypto.pseudoRandomBytes(32, (err, raw) => {
try{
cb(err, err ? undefined : crypto.createHash('MD5').update(raw).digest('hex'));
} catch(err) {
cb(err);
}
});
};
Test (running in mocha)
it('Crypto Error: createHash', function () {
const crypto = require('crypto');
const expectedError = new Error('stub error occurred');
let cryptoStub = sinon.stub(crypto, 'createHash').throws(expectedError);
let callback = sinon.spy();
getFileName(null, null, callback);
cryptoStub.restore();
sinon.assert.calledWith(callback, expectedError);
});
I would expect the above test to throw once createHash gets called. If I move the crypto.createHash call outside of the callback (before the pseudoRandomNumber call) it works just fine. I a bit of a newbie so my basic understanding of what sinon and nodejs are doing could be completely wrong. Any help would be much appreciated.
The reason why it seems like createHash() wasn't called was because you were making an assertion before the callback call was complete due to asynchronous function.
Promise with async/await will work. Another method which doesn't involve changing your module to use promise is to do your assertions within the callback.
it('Crypto Error: createHash', function (done) {
const crypto = require('crypto');
const expectedError = new Error('stub error occurred');
let cryptoStub = sinon.stub(crypto, 'createHash').throws(expectedError);
getFileName(null, null, function (err, hash) {
sinon.assert.match(err, expectedError);
cryptoStub.restore();
done();
});
});
This way, you can check that the callback is called with the expected error. One way to confirm this is you can change line 4 to .throws('some other error') and the test will fail.
The problem is that crypto.pseudoRandomBytes() is an async function, so the rest of your test code executes before your callback. That way, your stub is restored before your function is actually used it.
In order to make it properly work, you should update your getFileName.js so it returns a promise - that way you can await it
module.exports = (req, file, cb) => {
return new Promise((resolve, reject) => {
crypto.pseudoRandomBytes(32, (err, raw) => {
try{
cb(err, err ? undefined : crypto.createHash('MD5').update(raw).digest('hex'));
resolve();
} catch(err) {
reject(cb(err));
}
});
});
};
and then in your test
// added async
it('Crypto Error: createHash', async () => {
const crypto = require('crypto');
const expectedError = new Error('stub error occurred');
let cryptoStub = sinon.stub(crypto, 'createHash').throws(expectedError);
let callback = sinon.spy();
await getFileName(null, null, callback);
// once we are here, the callback has already been executed and the promise that getFileName resolved.
cryptoStub.restore();
sinon.assert.calledWith(callback, expectedError);
});
My code is:
async function run() {
await sleep(1);
fs.readFile('list.txt', 'utf8', function (err, text) {
console.log(text);
await sleep(5);
});
}
run();
await sleep(1) is fine, but await sleep(5) results in:
SyntaxError: async is only valid in async function
Is fs.readFile the problem? How do I use await in such a situation?
The above is a reduced example to test. In my actual usage, I need to put the await sleep in a callback deeply nested inside a few more asynchronous functions.
I'm not sure what sleep is doing, but the problem in your question is happening because the callback to fs.readfile is not an async function and the that is where await sleep(5) is.
async function run() {
await sleep(1);
fs.readFile('list.txt', 'utf8', function (err, text) {
console.log(text); /* ^this isn't an async function */
await sleep(5);
});
}
You could make that error go away by passing an async function as a callback, however mixing promise and callback flows is not recommended and leads to problems, especially when handling errors. It prevents you from chaining the promise and catching errors from the inner await outside of run().
A better approach is to wrap fs.readFile() in a promise and await that.
async function run() {
await sleep(1);
const text = await new Promise((resolve, reject) => {
fs.readFile('list.txt', 'utf8', function (err, text) {
if (err) reject(err) else resolve(text);
});
});
console.log(text);
await sleep(5);
}
This will allow you to catch any errors in much more robust way with:
run()
.then(() => /*...*/)
.catch(err => /* handle error */
and avoid unhandled rejections.
The above is a reduced example to test. In my actual usage, I need to put the await sleep in a callback deeply nested inside a few more asynchronous functions.
Callback based APIs shouldn't be augmented with async..await or promises in general when there is a chance to stick to promises (may not be possible if a callback is called more than once). This results in poor control flow and error handling.
Once there is async callback function inside run, it's impossible to chain nested promise with run().then(...). Errors from nested promises may remain unhandled as well and result in UnhandledPromiseRejectionWarning.
The proper way is to move from callbacks to promises and use promise control flow. Generally this can be achieved with promise constructor:
async function run() {
await sleep(1);
const text = await new Promise((resolve, reject) => {
fs.readFile('list.txt', 'utf8', function (err, text) {
if (err) reject(err) else resolve(text);
});
});
console.log(text);
await sleep(5);
}
There are numerous ways to get promises from Node callback based APIs, e.g. fs provides promise API since Node 10:
async function run() {
await sleep(1);
const text = await fs.promises.readFile('list.txt', 'utf8');
console.log(text);
await sleep(5);
}
If your function includes an "await", you must prefix your function declaration with "async".
NOTE: By making your function an "async" function a promise is always returned. So if you return a string, that string will get automatically wrapped in a Promise object.
You can therefore return a Promise manually (for clarity). A simple resolved promise is sufficient:
async function foo(){
await bar();
return Promise.resolve('optional'); // optionally resolve with a value
}
The keyword "async" always goes before the keyword "function":
var foo = async function(){
await bar();
return Promise.resolve('optional'); // optionally resolve with a value
}
... and well for arrow functions:
var foo = sally( async () => {
await bar();
return Promise.resolve('optional'); // optionally resolve with a value
})
To retrieve the return value:
foo().then(val => console.log(val) ); // prints "optional" to console
You have to use aysnc key before function start. await keyword
will not work with normal function.
async function run() {
await sleep(1);
fs.readFile('list.txt', 'utf8', async function (err, text) {
console.log(text);
await sleep(5);
});
}
run();