let arr = [];
function getData(fileName, type) {
return fs.readFile(fileName,'utf8', (err, data) => {
if (err) throw err;
return new Promise(function(resolve, reject) {
for (let i = 0; i < data.length; i++) {
arr.push(data[i]);
}
resolve();
});
});
}
getData('./file.txt', 'sample').then((data) => {
console.log(data);
});
When I use above code and run it in command line using nodejs I get following error.
getData('./file.txt', 'sample').then((data) => {
^
TypeError: Cannot read property 'then' of undefined
How can I solve this?
You'll want to wrap the entire fs.readFile invocation inside a new Promise, and then reject or resolve the promise depending on the callback result:
function getData(fileName, type) {
return new Promise(function(resolve, reject){
fs.readFile(fileName, type, (err, data) => {
err ? reject(err) : resolve(data);
});
});
}
[UPDATE] As of Node.js v10, you can optionally use the built-in Promise implementations of the fs module by using fs.promises.<API>. In the case of our readFile example, we would update our solution to use fs.promises like this:
function getData(fileName, type) {
return fs.promises.readFile(fileName, {encoding: type});
}
Nobody told about util.promisify so I'm going to post, however old the question is.
Why are you having this message?
getData('./file.txt', 'sample').then((data) => {
^
TypeError: Cannot read property 'then' of undefined
getData is a wrapper for fs.readFile file here. fs.readfile is not a thenable (it does not implement a then function). It is built on the other pattern, the callback pattern. The most well-known thenable are Promises, and that's what you want to get from readFile I believe. A little reminder: Mozilla - Promises
So what you can do is either implement it yourself as did #hackerrdave or I would suggest using promisify: this function is a built-in function of Node.js which was implemented to transform the callback-based function into promised based. You will find it here: Node.js Documentation for util.promisfy
It basically does the same as #hackerrdave but it's more robust and built-in node util.
Here's how to use it:
const util = require('util');
const fs = require('fs');
const readFile = util.promisify(fs.readFile)
readFile("path/to/myfile").then(file => console.log(file))
Here is a one-liner as of node 10.2.0:
(async () => console.log(String(await require('fs').promises.readFile('./file.txt'))))();
Yes, it is now out of the box.
As of Node 12+, you can use the fs.promises API.
See an example below:
const { readFile } = require('fs').promises
readFile('./file.txt', { encoding: 'utf8' })
.then((data) => console.log(data))
.catch((error) => console.error(error));
Using async/await
const { readFile } = require('fs').promises
async function readFile(filePath) {
try {
const data = await readFile(filePath, { encoding: 'utf8' })
console.log(data)
} catch (error) {
console.error(error.message)
}
}
readFile('./file.txt')
const getData = (fileName, type) =>
new Promise((resolve, reject) =>
fs.readFile(fileName, type, (err, data) => {
//if has error reject, otherwise resolve
return err ? reject(err) : resolve(data);
})
);
getData('./file.txt', 'utf8')
.then(data => console.log('Data: ', data))
.catch(error => console.log('Error: ', error));
Update for current node As of node 10.0.0 you can now use fs.promises:
const fs = require('fs')
(async function(){
var fileContents = await fs.promises.readFile(FILENAME)
var data = JSON.parse(fileContents)
})()
Related
I am trying to parse data from a .csv file, and save it to an array for later use.
I understand the concept of promises, but I have no idea what am I missing in my code that I cannot resolve the Promise and get the value (the string in the .csv file). It while I can view all the data inside the promise (.on('data')) from debugging mode, I just can't save it in order to use it later in my 'try&catch'.
const fs = require("fs");
const csv = require("csv-parser");
const { resolve } = require("path");
async function readCSV(filepath) {
return new Promise(async (resolve, reject) => {
await fs
.createReadStream(filepath)
.pipe(csv())
.on("data", (data) => {
results.push(data);
})
.on("error", (error) => reject(results))
.on("end", () => {
resolve(results);
});
});
}
const results = [];
const csvFilePath =
"/languages.csv";
try {
const languages = readCSV(csvFilePath).then((res) => {
return res;
});
console.log(languages);
} catch (e) {
console.log(e);
}
and the output on the console is:
>Promise {<pending>}
No debugger available, can not send 'variables'
** That's from the debugging mode when I pause inside the promise:
https://i.stack.imgur.com/H9nHi.png
You can't try catch a returned promise without the await keyword in an async function.
If you're returning a promise, you need to use the .catch method on the promise.
Also, when you're logging languages you're doing so before the promise resolves because you're not using the await keyword.
I'm sure the promise resolves. Instead, log res inside the .then method.
const fs = require("fs");
const csv = require("csv-parser");
const results = [];
function readCSV(filepath) {
return new Promise((resolve, reject) => {
fs
.createReadStream(filepath)
.pipe(csv())
.on("data", (data) => {
results.push(data);
})
.on("error", (error) => reject(results))
.on("end", () => {
resolve(results);
});
});
}
const csvFilePath = "./languages.csv";
(async () => {
const output = await readCSV(csvFilePath);
console.log(output)
})();
I have a node.js script that reads in some JS code from a file, and passes it to an eval(). The code that passes the javascript to the eval is as follows:
// read javascript code from file
var outputbuffer = '';
function output(data) {
outputbuffer += data + '\n';
}
eval(javascriptCodeFromFile);
// do stuff with outputbuffer
The javascript code in the file is as follows:
var fs = require('fs');
fs.readFile('myfile.txt', function(err, data) {
if (err) {
output('Error reading file');
}
else {
output(data.toString());
}
});
The problem is that the eval exits before the file read completes, as the file read is asyncronous. The eval simply starts the file read, and then exits, not waiting from the file read to complete. How can I make the eval wait for the callback before exiting? I have looked at promises and they seem PROMISing (pun intended) but I need someone to guide me in the right direction.
Leaving all the downsides of eval, here is a direct answer to your question.
Create a promise for keeping the event loop of the main thread busy.
Resolve the promise from within the script being eval. The script will have access to the resolve and reject` functions of the Promise since the evaluating script is within the scope of the promise.
let p = new Promise((resolve, reject) => eval(javascriptCodeFromFile));
p.then(()=> console.log(outputbuffer))
.catch((e) => console.log('Rejected ::' + outputBuffer));
Here is a runnable example that demonstrates the main thread waiting for an asynchronous execution happens inside eval
let jsCode = `
setTimeout(() => (resolve('Hello from eval after two seconds')), 2000);
`;
console.log('waiting...');
let p = new Promise((resolve, reject) => {
eval(jsCode);
})
p.then((data)=> console.log(data));
It's fine to have your eval'd code return a promise.
example:
const code = `
new Promise((resolve, reject) => {
var fs = require('fs');
fs.readFile('myfile.txt', function (err, data) {
if (err) {
reject('Error reading file');
}
else {
resolve(data.toString());
}
});
});
`;
console.log('starting...');
eval(code)
.then(str => console.log(str))
.catch(err => console.error(err));
It's also fine to make the Promise external to the eval and just call the resolve or reject from the eval'd code:
e.g.:
const code = `
var fs = require('fs');
fs.readFile('myfile.txt', function (err, data) {
if (err) {
reject(err);
}
else {
resolve(data.toString());
}
});
`;
console.log('starting...');
new Promise((resolve, reject) => eval(code))
.then(str => console.log(str))
.catch(err => console.error(err));
Or the same thing with await and calling the async version of readFile
const code = `
require('fs').promises.readFile('myfile.txt')
`;
console.log('starting...');
main().then(() => console.log('...done'));
async function main() {
try {
let result = await eval(code);
console.log(result);
} catch(err) {
console.error(err);
}
}
I would like to use async/await with some filesystem operations. Normally async/await works fine because I use babel-plugin-syntax-async-functions.
But with this code I run into the if case where names is undefined:
import fs from 'fs';
async function myF() {
let names;
try {
names = await fs.readdir('path/to/dir');
} catch (e) {
console.log('e', e);
}
if (names === undefined) {
console.log('undefined');
} else {
console.log('First Name', names[0]);
}
}
myF();
When I rebuild the code into the callback hell version everything is OK and I get the filenames.
Thanks for your hints.
Native support for async/await fs functions since Node 11
Since Node.JS 11.0.0 (stable), and version 10.0.0 (experimental), you have access to file system methods that are already promisify'd and you can use them with try catch exception handling rather than checking if the callback's returned value contains an error.
The API is very clean and elegant! Simply use the .promises member of fs object:
import fs from 'fs';
async function listDir() {
try {
return await fs.promises.readdir('path/to/dir');
} catch (err) {
console.error('Error occurred while reading directory!', err);
}
}
listDir();
Starting with node 8.0.0, you can use this:
const fs = require('fs');
const util = require('util');
const readdir = util.promisify(fs.readdir);
async function myF() {
let names;
try {
names = await readdir('path/to/dir');
} catch (err) {
console.log(err);
}
if (names === undefined) {
console.log('undefined');
} else {
console.log('First Name', names[0]);
}
}
myF();
See https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original
Node.js 8.0.0
Native async / await
Promisify
From this version, you can use native Node.js function from util library.
const fs = require('fs')
const { promisify } = require('util')
const readFileAsync = promisify(fs.readFile)
const writeFileAsync = promisify(fs.writeFile)
const run = async () => {
const res = await readFileAsync('./data.json')
console.log(res)
}
run()
Promise Wrapping
const fs = require('fs')
const readFile = (path, opts = 'utf8') =>
new Promise((resolve, reject) => {
fs.readFile(path, opts, (err, data) => {
if (err) reject(err)
else resolve(data)
})
})
const writeFile = (path, data, opts = 'utf8') =>
new Promise((resolve, reject) => {
fs.writeFile(path, data, opts, (err) => {
if (err) reject(err)
else resolve()
})
})
module.exports = {
readFile,
writeFile
}
...
// in some file, with imported functions above
// in async block
const run = async () => {
const res = await readFile('./data.json')
console.log(res)
}
run()
Advice
Always use try..catch for await blocks, if you don't want to rethrow exception upper.
As of v10.0, you can use fs.Promises
Example using readdir
const { promises: fs } = require("fs");
async function myF() {
let names;
try {
names = await fs.readdir("path/to/dir");
} catch (e) {
console.log("e", e);
}
if (names === undefined) {
console.log("undefined");
} else {
console.log("First Name", names[0]);
}
}
myF();
Example using readFile
const { promises: fs } = require("fs");
async function getContent(filePath, encoding = "utf-8") {
if (!filePath) {
throw new Error("filePath required");
}
return fs.readFile(filePath, { encoding });
}
(async () => {
const content = await getContent("./package.json");
console.log(content);
})();
You might produce the wrong behavior because the File-Api fs.readdir does not return a promise. It only takes a callback. If you want to go with the async-await syntax you could 'promisify' the function like this:
function readdirAsync(path) {
return new Promise(function (resolve, reject) {
fs.readdir(path, function (error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
and call it instead:
names = await readdirAsync('path/to/dir');
This is the TypeScript version to the question. It is usable after Node 11.0:
import { promises as fs } from 'fs';
async function loadMonoCounter() {
const data = await fs.readFile('monolitic.txt', 'binary');
return Buffer.from(data);
}
Node v14.0.0 and above
you can just do:
import { readdir } from "fs/promises";
just like you would import from "fs"
see this PR for more details: https://github.com/nodejs/node/pull/31553
I have this little helping module that exports promisified versions of fs functions
const fs = require("fs");
const {promisify} = require("util")
module.exports = {
readdir: promisify(fs.readdir),
readFile: promisify(fs.readFile),
writeFile: promisify(fs.writeFile)
// etc...
};
Here is what worked for me:
const fsp = require('fs-promise');
(async () => {
try {
const names = await fsp.readdir('path/to/dir');
console.log(names[0]);
} catch (e) {
console.log('error: ', e);
}
})();
This code works in node 7.6 without babel when harmony flag is enabled: node --harmony my-script.js. And starting with node 7.7, you don't even need this flag!
The fsp library included in the beginning is just a promisified wrapper for fs (and fs-ext).
I’m really exited about what you can do in node without babel these days! Native async/await make writing code such a pleasure!
UPDATE 2017-06: fs-promise module was deprecated. Use fs-extra instead with the same API.
Recommend using an npm package such as https://github.com/davetemplin/async-file, as compared to custom functions. For example:
import * as fs from 'async-file';
await fs.rename('/tmp/hello', '/tmp/world');
await fs.appendFile('message.txt', 'data to append');
await fs.access('/etc/passd', fs.constants.R_OK | fs.constants.W_OK);
var stats = await fs.stat('/tmp/hello', '/tmp/world');
Other answers are outdated
You can use the simple and lightweight module https://github.com/nacholibre/nwc-l it supports both async and sync methods.
Note: this module was created by me.
I am using a package called Okrabyte to extract words from each image file in a folder. The result should be a new array containing the extracted text that I can use in other functions.
When I run this:
var fs = require("fs");
var okrabyte = require("okrabyte");
fs.readdir("imgs/", function(err, files){
files.map((file)=>{
okrabyte.decodeBuffer(fs.readFileSync("imgs/"+ file), (err, data)=>{
let splitWords = data.split(" ");
let word = splitWords[0].substr(1);
console.log(word);
})
})
})
the console logs each word. To return an array with those words I've tried the following:
async function words() {
await fs.readdir("imgs/", function (err, files) {
return files.map(async (file) => {
await okrabyte.decodeBuffer(fs.readFileSync("imgs/" + file), async (err, data) => {
let splitWords = data.split(" ");
let word = splitWords[0].substr(1);
return word
})
})
})
}
var testing = await words();
console.log(testing);
This gives undefined I've tried turning everything into a promise, I've tried async-await, I've tried pushing each word into a new array and returning that array in closure but nothing works - what am I doing wrong??
If your map function is async then it's returning a promise, so your mapped array is in fact an array of promises. But you can then use a Promise.all to get the resolved values of that array.
Additionally, you're trying to await the call to fs.readdir and okrabyte.decodeBuffer, which both accept a callback and do not return a promise. So if you want to use a promise there you'll have to wrap them in a promise constructor manually.
Here's how I would do it:
async function words() {
// Wrap `fs` call into a promise, so we can await it:
const files = await new Promise((resolve, reject) => {
fs.readdir("imgs/", (err, files) => { err ? reject(err) : resolve(files); });
});
// Since map function returns a promise, we wrap into a Promise.all:
const mapped = await Promise.all(files.map((file) => {
// Wrap okrabyte.decodeBuffer into promise, and return it:
return new Promise((resolve, reject) => {
okrabyte.decodeBuffer(fs.readFileSync("imgs/" + file), (err, data) => {
if (err) return reject(err);
const splitWords = data.split(" ");
const word = splitWords[0].substr(1);
resolve(word);
})
})
}))
// Mapped is now an array containing each "word".
return mapped;
}
var testing = await words();
// Should now log your array of words correctly.
console.log(testing);
You should not be using async-await that way. That should be used when you are dealing with promises. The library okrabyte uses the concept of callbacks.
I suggest you follow this approach:
(1) Enclose the okrabyte.decodeBuffer part in a function that returns a promise that resolves in the callback.
(2) Use files.map to generate an array of promises calling the function you defined in (1)
(3) Use Promise.all to wait for all promises to execute and finish before moving on to dealing with all the words.
Walkthrough:
Part 1
const processWord = (file) => {
return new Promise((resolve, reject) => {
okrabyte.decodeBuffer(fs.readFileSync("imgs/"+ file), (err, data)=>{
if (err) {
reject(err); // <--- reject the promise if there was an error
return;
}
let splitWords = data.split(" ");
let word = splitWords[0].substr(1);
resolve(word); // <--- resolve the promise with the word
})
});
}
You make a function that wraps the decoding part into a promise that eventually resolves with the word (or is rejected with an error).
Part 2
const promises = files.map((file)=>{
return processWord(file);
})
The above will generate an array of promises.
Part 3
fs.readdir("imgs/", function(err, files){
const promises = files.map((file)=>{
return processWord(file);
})
Promise.all(promises)
.then(responses => {
// responses holds a list of words
// You will get each word accessing responses[0], responses[1], responses[2], ...
console.log(responses);
})
.catch(error => {
console.log(error); // Deal with the error in some way
});
})
The above uses Promise.all to wait for all promises to resolve before going to the then() block, assuming no errors occurred.
You can further isolate the construct above in a method that will return a promise with a list of all the words, much in the same fashion that was done in the processWord function from Part 1. That way, you can finally use async-await if you wish, instead of handling things in the then() block:
const processEverything = () => {
return new Promise((resolve, reject) => {
fs.readdir("imgs/", function(err, files){
const promises = files.map((file)=>{
return processWord(file);
})
Promise.all(promises)
.then(responses => {
resolve(responses);
})
.catch(error => {
reject(error);
});
})
});
};
const words = await processEverything();
console.log(words);
You're returning word value to the enclosed function but not map() function.
Hope this code help you.
async function words() {
global.words = [];
await fs.readdir("imgs/", function(err, files){
return files.map( async(file)=>{
await okrabyte.decodeBuffer(fs.readFileSync("imgs/"+ file), async (err, data)=>{
let splitWords = data.split(" ");
let word = splitWords[0].substr(1);
global.words.push(word);
})
})
})
}
var testing = await words();
testing = global.words;
console.log(testing);
I'm trying to promisify node-adodb using bluebirdjs.
I've tried this:
import Promise from 'bluebird'
import ADODB from 'node-adodb'
const db = ADODB.open(`...`)
const dbQuery = db.query(`...`)
const dbQueryOn = Promise.promisify(dbQuery.on, { context: dbQuery })
dbQueryOn('done').then(data => {
console.log('data =', data)
}).catch(err => {
console.log('err =', err)
})
The data is returned, but it comes via the .catch() not the .then() method.
How do I get node-adodb working with promises..?
I'm not familiar with node-adodb, but from its documentation it seems it uses a non-conventional way of returning errors and results (using event-like emitters).
Bluebird's promisify requires the regular Node.js callback convention (first argument represents errors, second argument represents the "result" value), so you can't use it in this situation.
But you can wrap it yourself:
const db = ADODB.open(`...`);
const runQuery = query => {
return new Promise((resolve, reject) => {
db.query(query)
.on('done', resolve)
.on('fail', reject);
});
}
// Usage:
runQuery(`...`).then(data => {
console.log('data =', data)
}).catch(err => {
console.log('err =', err)
})