So in Node we use async functions. And to do that we use callback functions as parameters. But how do I execute the final function after which I want to terminate the code? Just pass the empty function? Here's example:
fs.mkdir('stuff', function(){
fs.readFile('readMe.txt', 'utf8', function(err, data) {
fs.writeFile('./stuff/writeMe.txt', data);
});
});
mkdir has callback function - all fine
readFile has callback function - all fine
writeFile has NOT callback function because that's the last thing I want to do, but then I get an error in console:
"DeprecationWarning: Calling an asynchronous function without callback is deprecated."
Should I every time I do that, pass an empty function as a callback to avoid the error? What's the best practice for this?
Should I every time I do that, pass an empty function as a callback to avoid the error?
No.
What's the best practice for this?
Pass in a function and handle any errors it reports. You also need to handle errors from mkdir and readFile, which currently you're ignoring.
E.g.:
fs.mkdir('stuff', function(err) {
if (err) {
// Do something with the fact mkdir failed
} else {
fs.readFile('readMe.txt', 'utf8', function(err, data) {
if (err) {
// Do something with the fact readFile failed
} else {
fs.writeFile('./stuff/writeMe.txt', data, function(err) {
if (err) {
// Do something with the fact writeFile failed
}
});
}
});
}
});
...which is a fair example of callback hell, which is part of the motivation of using promises instead. You could promisify the fs API (using any of several libs, such as promisify) and do it like this:
fsp.mkdir('stuff')
.then(() => fsp.readFile('readMe.txt', 'utf8'))
.then(data => fsp.writeFile('./stuff/writeMe.txt', data))
.catch(err => {
// Do something with the fact something failed
});
...where fsp is a placeholder for the promisified fs API.
In Node 8.x+, you could use async/await to write synchronous-looking code with those promisified APIs:
// This must be inside an `async` function
try {
fsp.mkdir('stuff');
const data = await fsp.readFile('readMe.txt', 'utf8');
await fsp.writeFile('./stuff/writeMe.txt', data);
} catch (err) {
// Do something with the fact something failed
}
You can use writeFileSync instead.
fs.mkdir('stuff', function(){
fs.readFile('readMe.txt', 'utf8', function(err, data) {
fs.writeFileSync('./stuff/writeMe.txt', data);
});
});
Related
Here is the code I'm using:
const { exec } = require("child_process");
exec("ls -la", (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
It returns "0" and then logs in the console the correct result.
But how to directly return the result instead of console.log?
Some answers have pointed out how to convert it into a Promise.
You can't return a value from that callback, because it would not be passed to anything. What you can do is defining a Promise that passes the stdout to the resolve method. Here's an example:
const { exec } = require("child_process");
function ls() {
return new Promise((resolve, reject) => {
exec("ls -la", (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
reject(error.message);
}
if (stderr) {
console.log(`stderr: ${stderr}`);
reject(stderr);
}
console.log(`stdout: ${stdout}`);
resolve(stdout);
});
});
}
What I am doing here is defining a function that creates a new Promise. The Promise will execute your code (the ls -la call), and will fire an exception if there is an error, rejecting the Promise, or it will solve the Promise if everything is fine, passing the stdout value.
You can then use this Promise with something like this:
ls().then((out) => {
console.log(out);
})
the out variable will contain your stdout.
If you want some function that returns that value, it should be awaited from this function. An example could be this:
async function unwrapLs() {
const stdout = await ls();
return stdout;
}
Note that you can only call unwrapLs() from inside an async function, because you have to await for its value. In fact, this would be equivalent to calling ls() by awaiting it, but you can only do it from inside an async function.
The problem you are experiencing is that it takes time to run an OS command (perhaps indefinite amount of time). Javascript will not just halt execution until this is done, so it is run asynchronously.
A value must be returned and must be returned to the caller before it completes. Using promises, the value returned is a promise.
Since you are calling with the Construct 2 Engine which does not have a mechanism for asynchronous calls, you will not get a result directly.
However, Construct 2 engine has a mechanism for calling back into it, using the Function object
Your javascript should look like this:
const { exec } = require("child_process");
function ls(callback) {
exec("ls -la", (error, stdout, stderr) => {
if (c2_callFunction)
c2_callFunction(callback, [error, stdout, stderr]);
});
}
ls('lsresult')
you can execute like this :
Browser.ExecJS("")
But, to get the results, you must define a Function object called 'lsresult', add parameters to the function (error, stdout, stderr) and handle the results there.
Documentation is here: https://www.construct.net/en/construct-2/manuals/construct-2/plugin-reference/function
I'm using the AWS Javascript SDK and I'm following the tutorial on how to send an SQS message. I'm basically following the AWS tutorial which has an example of the sendMessage as follows:
sqs.sendMessage(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data.MessageId);
}
});
So the sendMessage function uses a callback function to output whether the operation was successful or not. Instead of printing to the console I want to return a variable, but every value I set is only visible within the callback function, even global variables like window.result are not visible outside the callback function. Is there any way to return values outside the callback?
The only workaround I've found at the moment is to set a data attribute in an HTML element, but I don't think it's really elegant solution.
I would suggest to use Promises and the new async and await keywords in ES2016. It makes your code so much easier to read.
async function sendMessage(message) {
return new Promise((resolve, reject) => {
// TODO be sure SQS client is initialized
// TODO set your params correctly
const params = {
payload : message
};
sqs.sendMessage(params, (err, data) => {
if (err) {
console.log("Error when calling SQS");
console.log(err, err.stack); // an error occurred
reject(err);
} else {
resolve(data);
}
});
});
}
// calling the above and getting the result is now as simple as :
const result = await sendMessage("Hello World");
With events, the initiator raises an event that will be received by those routines that have elected to receive that event. The receiver specifies what events it will receive from what initiators.
With callbacks, the routine after completion notifies the caller of the completion.
So I am confused where should I use events or where should I use the callbacks as I can accomplish what a callback do with events but resulting in a lot of events created in the application.
What should be a good approach to follow while coding, to use events or callbacks?
Events - for things that can happen many times.
Callbacks (or promises) - for things that can happen once.
So for example, when you have a function that you call because you need a current temperature taken from some API, that function should either return a promise or take a callback that can later be called with the right value (or error).
If, on the other hand, you have a function that you call because you need to get a new temperature every time when it changes, then this function should return an event emitter (or take an event handler to attach to some internal event emitter).
Now, the question on when to use callbacks and when to use promises is a little bit more tricky because they are good for the same kinds of situations - when you want to know a result of some asynchronous operation (some data or error). Since both work for the same situations let's consider two examples of reading the contents of a file.
First, with callbacks:
let fs = require('fs');
fs.readFile('a.txt', 'utf-8', (err, data) => {
if (err) {
console.log('Error:', err.message);
} else {
console.log('Data:', data.trim());
}
});
If there is no file it will print:
Error: ENOENT: no such file or directory, open 'a.txt'
If there is a file it will print:
Data: Contents of a.txt
Now, the same with promises:
let fs = require('mz/fs');
fs.readFile('b.txt', 'utf-8')
.then(data => {
console.log('Data:', data.trim());
})
.catch(err => {
console.log('Error:', err.message);
});
It works exactly the same as the previous example.
For that simple example the difference may not be very obvious but what if you wanted to have a function that abstracts some of that logic away.
For example this, with callbacks:
let fs = require('fs');
function a(cb) {
fs.readFile('b.txt', 'utf-8', (err, data) => {
if (err) {
return cb('a() error: ' + err.message);
}
cb(null, 'a() data: ' + data.trim());
});
}
a((err, data) => {
if (err) {
console.log('Error:', err);
} else {
console.log('Data:', data);
}
});
It will print either this
Error: a() error: ENOENT: no such file or directory, open 'a.txt'
or something like this:
Data: a() data: Contents of a.txt
Now, what is different with promises is that you can store it in a variable, return it from a function or pass it as an argument to some other function before attaching the success/error handlers. For example:
let fs = require('mz/fs');
function a() {
return fs.readFile('a.txt', 'utf-8')
.then(data => 'a() data: ' + data.trim())
.catch(err => Promise.reject('a() error: ' + err.message));
}
let promise = a();
promise.then(data => console.log('Data:', data))
.catch(err => console.log('Error:', err));
It works the same, it is written in a different style that you may or may not find more readable, but the difference is that now you don't have to attach a callback at the time of calling the a() function. You can do it somewhere else.
If you didn't want to change the error message, it would be this with callbacks:
function a(cb) {
fs.readFile('a.txt', 'utf-8', (err, data) => {
if (err) {
return cb(err);
}
cb(null, 'a() data: ' + data.trim());
});
and this with promises:
function a() {
return fs.readFile('a.txt', 'utf-8')
.then(data => 'a() data: ' + data.trim());
}
Another difference is that if you have a function that returns a promise, you can use a new await keyword inside of a async function like this:
async function x() {
try {
console.log('Data:', await a());
} catch (err) {
console.log('Error:', err);
}
}
You cannot use await with a function that doesn't return a promise.
It gets very convenient for example when you need to read file a.txt to get another filename that it contains, and then read that other file and print its contents while handling all errors in more complex situations.
To use async and await with Node v7.x you need to use the --harmony flag, see:
http://node.green/#async-functions
What I want to do is read a file and then be able to perform other operations with that information as I write the file. For example:
read file
write file and at the same time perform MD5 hash, digital signing etc.
I could use fs.readfile and fs.writefile as one operation and just copy the file from the web server to my computer, but I don't think I could still do these same operations. Anyway, skipping the in between stuff. How do I use fs.readfile and writefile to create two separate functions to copy a file? Here is what I have been working on, and yes I've read these forums extensively in search of an answer.
var fs = require('fs');
function getData(srcPath) {
fs.readFile(srcPath, 'utf8', function (err, data) {
if (err) throw err;
return data;
}
);
}
function writeData(savPath, srcPath) {
fs.writeFile (savPath, (getData(srcPath)), function(err) {
if (err) throw err;
console.log('complete');
}
);
}
//getData ('./test/test.txt');
writeData ('./test/test1.txt','./test/test.txt');
I want to be able to download files of any type and just make raw copies, with md5 hash etc attached to a JSON file. That will probably be a question for later though.
As suggested by dandavis in his comment, readFile does nothing because it is an asynchronous call. Check out this answer for additional information on what that means.
In short, an async call will never wait for the result to return. In your example, getData does not wait for readFile() to return the result you want, but will finish right away. Async calls are usually handled by passing callbacks, which is the last parameter to readFile and writeFile.
In any case, there are two ways to do this:
1.Do it asynchronously (which is the proper way):
function copyData(savPath, srcPath) {
fs.readFile(srcPath, 'utf8', function (err, data) {
if (err) throw err;
//Do your processing, MD5, send a satellite to the moon, etc.
fs.writeFile (savPath, data, function(err) {
if (err) throw err;
console.log('complete');
});
});
}
2.Do it synchronously. Your code won't have to change much, you will just need to replace readFile and writeFile by readFileSync and writeFileSync respectively. Warning: using this method is not only against best practises, but defies the very purpose of using nodejs (unless of course you have a very legitimate reason).
Edit: As per OP's request, here is one possible way to separate the two methods, e.g., using callbacks:
function getFileContent(srcPath, callback) {
fs.readFile(srcPath, 'utf8', function (err, data) {
if (err) throw err;
callback(data);
}
);
}
function copyFileContent(savPath, srcPath) {
getFileContent(srcPath, function(data) {
fs.writeFile (savPath, data, function(err) {
if (err) throw err;
console.log('complete');
});
});
}
This way, you are separating the read part (in getFileContent) from the copy part.
I had to use this recently, so I converted verybadallocs answer to promises.
function readFile (srcPath) {
return new Promise(function (resolve, reject) {
fs.readFile(srcPath, 'utf8', function (err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
}
function writeFile (savPath, data) {
return new Promise(function (resolve, reject) {
fs.writeFile(savPath, data, function (err) {
if (err) {
reject(err)
} else {
resolve()
}
})
})
}
Then using them is simple.
readFile('path').then(function (results) {
results += ' test manipulation'
return writeFile('path', results)
}).then(function () {
//done writing file, can do other things
})
Usage for async/await
const results = await readFile('path')
results += ' test manipulation'
await writeFile('path', results)
// done writing file, can do other things
To read and write a file with Non-blocking or Asynchronous way, you can use the advance features of es6 or higher like Promise or Async/await, but you must keep eye on Polyfills (https://javascript.info/polyfills) or if there are only a couple of read/write you can use call back Hell.
function readFiles(){
fs.readFile('./txt/start.txt', 'utf-8', (err, data1)=>{
if(err) return console.log(err);
fs.readFile(`./txt/${data1}.txt`, 'utf-8', (err, data2)=>{
if(err) return console.log(err);
fs.readFile('./txt/append.txt', 'utf-8', (err, data3)=>{
if(err) return console.log(err);
writeFile('./txt/final.txt', `${data2}\n${data3}`);
});
});
});
}
function writeFile(path, data){
fs.writeFile(path,data,'utf-8',err=>{
if(err){
console.log(err);
}
})
}
readFiles();
This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 7 days ago.
var content;
fs.readFile('./Index.html', function read(err, data) {
if (err) {
throw err;
}
content = data;
});
console.log(content);
Logs undefined, why?
To elaborate on what #Raynos said, the function you have defined is an asynchronous callback. It doesn't execute right away, rather it executes when the file loading has completed. When you call readFile, control is returned immediately and the next line of code is executed. So when you call console.log, your callback has not yet been invoked, and this content has not yet been set. Welcome to asynchronous programming.
Example approaches
const fs = require('fs');
// First I want to read the file
fs.readFile('./Index.html', function read(err, data) {
if (err) {
throw err;
}
const content = data;
// Invoke the next step here however you like
console.log(content); // Put all of the code here (not the best solution)
processFile(content); // Or put the next step in a function and invoke it
});
function processFile(content) {
console.log(content);
}
Or better yet, as Raynos example shows, wrap your call in a function and pass in your own callbacks. (Apparently this is better practice) I think getting into the habit of wrapping your async calls in function that takes a callback will save you a lot of trouble and messy code.
function doSomething (callback) {
// any async callback invokes callback with response
}
doSomething (function doSomethingAfter(err, result) {
// process the async result
});
There is actually a Synchronous function for this:
http://nodejs.org/api/fs.html#fs_fs_readfilesync_filename_encoding
Asynchronous
fs.readFile(filename, [encoding], [callback])
Asynchronously reads the entire contents of a file. Example:
fs.readFile('/etc/passwd', function (err, data) {
if (err) throw err;
console.log(data);
});
The callback is passed two arguments (err, data), where data is the contents of the file.
If no encoding is specified, then the raw buffer is returned.
SYNCHRONOUS
fs.readFileSync(filename, [encoding])
Synchronous version of fs.readFile. Returns the contents of the file named filename.
If encoding is specified then this function returns a string. Otherwise it returns a buffer.
var text = fs.readFileSync('test.md','utf8')
console.log (text)
function readContent(callback) {
fs.readFile("./Index.html", function (err, content) {
if (err) return callback(err)
callback(null, content)
})
}
readContent(function (err, content) {
console.log(content)
})
Using Promises with ES7
Asynchronous use with mz/fs
The mz module provides promisified versions of the core node library. Using them is simple. First install the library...
npm install mz
Then...
const fs = require('mz/fs');
fs.readFile('./Index.html').then(contents => console.log(contents))
.catch(err => console.error(err));
Alternatively you can write them in asynchronous functions:
async function myReadfile () {
try {
const file = await fs.readFile('./Index.html');
}
catch (err) { console.error( err ) }
};
This line will work,
const content = fs.readFileSync('./Index.html', 'utf8');
console.log(content);
var data = fs.readFileSync('tmp/reltioconfig.json','utf8');
use this for calling a file synchronously,
without encoding its showing output as a buffer.
As said, fs.readFile is an asynchronous action. It means that when you tell node to read a file, you need to consider that it will take some time, and in the meantime, node continued to run the following code. In your case it's: console.log(content);.
It's like sending some part of your code for a long trip (like reading a big file).
Take a look at the comments that I've written:
var content;
// node, go fetch this file. when you come back, please run this "read" callback function
fs.readFile('./Index.html', function read(err, data) {
if (err) {
throw err;
}
content = data;
});
// in the meantime, please continue and run this console.log
console.log(content);
That's why content is still empty when you log it. node has not yet retrieved the file's content.
This could be resolved by moving console.log(content) inside the callback function, right after content = data;. This way you will see the log when node is done reading the file and after content gets a value.
From Node v8
Use the built in promisify library to make these old callback functions more elegant.
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);
async function doStuff() {
try {
const content = await readFile(filePath, 'utf8');
console.log(content);
} catch (e) {
console.error(e);
}
}
From Node v10
You can use the promises version of fs API:
import { promises as fs } from 'fs';
async function doStuff() {
try {
const content = await fs.readFile(filePath, 'utf8');
console.log(content);
} catch (e) {
console.error(e);
}
}
const fs = require('fs')
function readDemo1(file1) {
return new Promise(function (resolve, reject) {
fs.readFile(file1, 'utf8', function (err, dataDemo1) {
if (err)
reject(err);
else
resolve(dataDemo1);
});
});
}
async function copyFile() {
try {
let dataDemo1 = await readDemo1('url')
dataDemo1 += '\n' + await readDemo1('url')
await writeDemo2(dataDemo1)
console.log(dataDemo1)
} catch (error) {
console.error(error);
}
}
copyFile();
function writeDemo2(dataDemo1) {
return new Promise(function(resolve, reject) {
fs.writeFile('text.txt', dataDemo1, 'utf8', function(err) {
if (err)
reject(err);
else
resolve("Promise Success!");
});
});
}
sync and async file reading way:
//fs module to read file in sync and async way
var fs = require('fs'),
filePath = './sample_files/sample_css.css';
// this for async way
/*fs.readFile(filePath, 'utf8', function (err, data) {
if (err) throw err;
console.log(data);
});*/
//this is sync way
var css = fs.readFileSync(filePath, 'utf8');
console.log(css);
Node Cheat Available at read_file.
var path = "index.html"
const readFileAsync = fs.readFileSync(path, 'utf8');
// console.log(readFileAsync)
using simple readFileSync works for me.
var fs = require('fs');
var path = (process.cwd()+"\\text.txt");
fs.readFile(path , function(err,data)
{
if(err)
console.log(err)
else
console.log(data.toString());
});
var content;
fs.readFile('./Index.html', function read(err, data) {
if (err) {
throw err;
}
content = data;
});
console.log(content);
This is just because node is asynchronous and it will not wait for the read function and as soon as the program starts it will console the value as undefined, Which is actually true because there is no value assigned to content variable.
To handle we can use promises, generators etc.
We can use promise in this way.
new Promise((resolve,reject)=>{
fs.readFile('./index.html','utf-8',(err, data)=>{
if (err) {
reject(err); // in the case of error, control flow goes to the catch block with the error occured.
}
else{
resolve(data); // in the case of success, control flow goes to the then block with the content of the file.
}
});
})
.then((data)=>{
console.log(data); // use your content of the file here (in this then).
})
.catch((err)=>{
throw err; // handle error here.
})
The following is function would work for async wrap or promise then chains
const readFileAsync = async (path) => fs.readFileSync(path, 'utf8');
you can read file by
var readMyFile = function(path, cb) {
fs.readFile(path, 'utf8', function(err, content) {
if (err) return cb(err, null);
cb(null, content);
});
};
Adding on you can write to file,
var createMyFile = (path, data, cb) => {
fs.writeFile(path, data, function(err) {
if (err) return console.error(err);
cb();
});
};
and even chain it together
var readFileAndConvertToSentence = function(path, callback) {
readMyFile(path, function(err, content) {
if (err) {
callback(err, null);
} else {
var sentence = content.split('\n').join(' ');
callback(null, sentence);
}
});
};
To put it roughly, you're dealing with node.js which is asynchronous in nature.
When we talk about async, we're talking about doing or processing info or data while dealing with something else. It is not synonymous to parallel, please be reminded.
Your code:
var content;
fs.readFile('./Index.html', function read(err, data) {
if (err) {
throw err;
}
content = data;
});
console.log(content);
With your sample, it basically does the console.log part first, thus the variable 'content' being undefined.
If you really want the output, do something like this instead:
var content;
fs.readFile('./Index.html', function read(err, data) {
if (err) {
throw err;
}
content = data;
console.log(content);
});
This is asynchronous. It will be hard to get used to but, it is what it is.
Again, this is a rough but fast explanation of what async is.
I like using fs-extra because all functions are promisified, right out of the box, so you can use await. So your code could look like this:
(async () => {
try {
const content = await fs.readFile('./Index.html');
console.log(content);
} catch (err) {
console.error(err);
}
})();