I use the following code which is working OK
var ncp = require('ncp').ncp;
function load(folderPath) {
ncp.limit = 16;
var path = require('path');
var localPath = path.join(__dirname, '../pl');
ncp(folderPath, localPath, {dereference: false}, function (err) {
if (err) {
return console.error(err);
}
console.log('done to save the files!');
});
};
I want to use promise instead of callback but when using the following I got error
var Promise = require('bluebird');
var ncp = Promise.promisifyAll(require('ncp').ncp);
function load(folderPath) {
ncp.limit = 16;
var localPath = path.join(__dirname, '../pl');
ncp(folderPath, localPath, {dereference: false})
.then(function (result) {
console.log('done to save the files!');
})
.catch(function (err) {
console.err(err);
});
};
The error is :
TypeError: Cannot read property 'then' of undefined
Promise.promisifyAll() is used to promisify objects. It will iterate the object and promisify all of the function properties on that object.
However, ncp is a function itself, it doesn't have properties, so promisifyAll() won't do. You're looking for promisify() which takes a function as an argument, and returns a promisified function.
So all you need to do is change
var ncp = Promise.promisifyAll(require('ncp').ncp);
to
var ncp = Promise.promisify(require('ncp').ncp);
Related
I tried and have looked at StackOverflow and the other posts are not answering my questions. This is a unique question. How can I get the path of the most recently uploaded file saved to a variable so it can be used later?
Code:
var pathtocheck = "C:\Users\user1\Downloads";
var path = require('path');
var fs = require('fs');
var getMostRecent = function (dir, cb) {
var dir = path.resolve(dir);
var files = fs.readdir(dir, function (err, files) {
var sorted = files.map(function(v) {
var filepath = path.resolve(dir, v);
return {
name:v,
time:fs.statSync(filepath).mtime.getTime()
};
})
.sort(function(a, b) { return b.time - a.time; })
.map(function(v) { return v.name; });
if (sorted.length > 0) {
cb(null, sorted[0]);
} else {
cb('Y U NO have files in this dir?');
}
})
}
await getMostRecent(pathtocheck, function (err, recent) {
if (err) console.error(err);
console.log(recent);
});
var lastdownloadedimage = ;
With callbacks, you have to write your code in the callback passed to getMostRecent, so
// Note: awaiting this takes no effect unless getMostRecent returns a promise.
getMostRecent(pathtocheck, function (err, recent) {
if (err) console.error(err);
console.log(recent);
var lastdownloadedimage = recent;
// Or just directly use `recent`
});
Or,
Async-await and Promise can also solve your issue, though I'm not sure how much you're familiar with them.
You can use Promisified version of file system API in Node.js v10 or above, by require('fs').promises instead of require('fs') (Documentation here)
Decalration of the functions like this:
// Also it's a good practice to use `const` or `let`, instead of `var`
const pathtocheck = "C:\Users\user1\Downloads";
const path = require('path');
const fs = require('fs');
const fsp = require('fs').promises;
// Decalre the function with `async` to use `await` in it
const getMostRecent = async function (dir) {
dir = path.resolve(dir);
const files = await fsp.readdir(dir)
const sorted = files.map(function(v) {
const filepath = path.resolve(dir, v);
return {
name:v,
time:fs.statSync(filepath).mtime.getTime()
// maybe you can use fsPromises.stat here
};
})
.sort(function(a, b) { return b.time - a.time; })
.map(function(v) { return v.name; });
if (sorted.length > 0) {
return sorted[0];
} else {
// Now you have no callbacks, so there are two options to return the error state.
// 1. Throw an Error with an error message
// 2. Return a special value such as `null` or `false`, which you can track it.
}
}; // <-- perhaps you need place a semicolon here.
And you call the function in async IIFE, wrapping anonymous async function to use await
(async function() {
const lastdownloadedimage = await getMostRecent(pathtocheck);
console.log(lastdownloadedimage)
})();
Or use Promise.then:
getMostRecent(pathtocheck).then(function(recent) {
var lastdownloadedimage = recent;
// Again, you can just directly use `recent`
})
Please excuse my noobness, but why isn't this working?
then() is never fired, and neither is error(). Promise seems to never resolve.
Any pointers appreciated. Thanks.
var Promise = require('bluebird');
var execFile = require('child_process').execFile;
execFile = Promise.promisify(execFile);
var IMAGE_DIR = "resources/assets/images";
var validImages = ['.jpg', '.png'];
... // setup omitted ...
execFile('find', [IMAGE_DIR], function (err, stdout, stderr) {
var images = [];
return new Promise(function(resolve) {
var fileList = stdout.split('\n');
images = fileList.filter(function (image) {
var ext = path.extname(image);
if (validImages.indexOf(ext) > -1) {
return image;
}
})
return resolve(images);
})
}).then(function () {
console.log(arguments);
}).catch(console.log.bind(console));
You're just not using the promisified version of execFile() properly.
You should be doing:
const Promise = require('bluebird');
const execFile = Promise.promisify(require('child_process').execFile);
execFile('find', [IMAGE_DIR]).then(function(stdout) {
// process result here
}).catch(function(err) {
// handle error here
});
If you need access to both stdout and stderr, then you have to pass the multiArgs option to .promisify().
const Promise = require('bluebird');
const execFile = Promise.promisify(require('child_process').execFile, {multiArgs: true});
execFile('find', [IMAGE_DIR]).then(function(args) {
let stdout = args[0];
let stderr = args[1];
// process result here
}).catch(function(err) {
// handle error here
});
I highly appreciate the answer given by jfriend000. Anyways, if you want a ES7 solution with async-await:
const Promise = require('bluebird');
const execFile = Promise.promisify(require('child_process').execFile
const find = async () => {
try{
let output = await execFile('find', [IMAGE_DIR]);
// handle your output with the variable
} catch(error) {
// handle your errors here
}
}
This is my module1:
var fs = require('fs');
var obj;
exports.module1= function(ret)
{
fs.readFile('source.json', 'utf8', function (err, data)
{
if (err) {
return console.error(err);
}
obj=JSON.parse(data);
console.log(obj);
return obj;
});
}
Module2:
var module1 = require('./module1.js');
var obj=module1.module1();
var callback = function () {
console.log(obj);
};
setTimeout(callback, 10000);
The obj of module2 is not getting updated with returned value of module1. I am newbie btw.
I believe ur problem is that fs.readFile is an async call and its return value will not be passed to the obj defined in Module2.
Just for reference u may pass an callback function to module1's export and then call when the file reading completes.
Module1:
var fs = require('fs');
var obj;
exports.module1= function(callback)
{
fs.readFile('source.json', 'utf8', function (err, data)
{
if (err) {
return console.error(err);
}
obj=JSON.parse(data);
console.log(obj);
callback(obj)
});
}
Module2:
var module1 = require('./module1.js');
var obj;
module1.module1(function(result){
obj = result;
});
var callback = function () {
console.log(obj);
};
setTimeout(callback, 10000);
You can share the object by passing it the the global context, which means the object will be usable from any file at any time, but the main JavaScript code (which requires the module) will be able to access it too. If you don't want it to access the code, comment this post and I'll make a script to do that.
The most simple way is to share the object :
global.__module_object = obj;
And anywhere you'll be able to access the object by doing global.__module_object.data = "Hello world !"; for example.
I've used the following code to call two modules, but the invoke action is called before the validate file (I saw in debug). What I should do to verify that validateFile is called before appHandler.invokeAction? Should I use a promise?
var validator = require('../uti/valid').validateFile();
var appHandler = require('../contr/Handler');
appHandler.invokeAction(req, res);
Update
this is the validate file code
var called = false;
var glob = require('glob'),
fs = require('fs');
module.exports = {
validateFile: function () {
glob("myfolder/*.json", function (err, files) {
var stack = [];
files.forEach(function (file) {
fs.readFile(file, 'utf8', function (err, data) { // Read each file
if (err) {
console.log("cannot read the file", err);
}
var obj = JSON.parse(data);
obj.action.forEach(function (crud) {
for (var k in crud) {
if (_inArray(crud[k].path, stack)) {
console.log("duplicate founded!" + crud[k].path);
break;
}
stack.push(crud[k].path);
}
})
});
});
});
}
};
Because glob and fs.readFile are async functions and appHandler.invokeAction is invoked during i/o from disk.
Promise is a good solution to solve this but an old school callback could do the job.
validator.validateFile().then(function() {
appHandler.invokeAction(req, res);
});
and for validate
var Promise = require("bluebird"), // not required if you are using iojs or running node with `--harmony`
glob = require('mz/glob'),
fs = require('mz/fs');
module.exports = {
validateFile: function () {
return glob("myfolder/*.json").then(function(files) {
return Promise.all(files.map(function(file) {
// will return an array of promises, if any of them
// is rejected, validateFile promise will be rejected
return fs.readFile(file).then(function (content) {
// throw new Error(''); if content is not valid
});
}));
})
}
};
If you want working with promise mz could help :)
As the fs.fileRead is async, you should put the code that you want to execute after validateFile to its callback.
The origin could be:
var validator = require('../uti/valid').validateFile();
var appHandler = require('../contr/Handler');
// create a new function that when execute, will call appHandler.invokeAction with req and res given to its arguments.
validator.validateFile(appHandler.invokeAction.bind(null, req, res));
The validator part should be:
var called = false;
var glob = require('glob'),
fs = require('fs');
module.exports = {
validateFile: function (callback) {
glob("myfolder/*.json", function (err, files) {
var stack = [];
// Use it to decide whether all files processed or not.
var filesToLoad = files.length;
files.forEach(function (file) {
fs.readFile(file, 'utf8', function (err, data) { // Read each file
--filesToLoad;
if (err) {
console.log("cannot read the file", err);
// If the invoke action doesn't depend on the result. You may want to call it here too.
}
var obj = JSON.parse(data);
obj.action.forEach(function (crud) {
for (var k in crud) {
if (_inArray(crud[k].path, stack)) {
console.log("duplicate founded!" + crud[k].path);
break;
}
stack.push(crud[k].path);
}
})
// Only called the callback after all files processed.
if (filesToLoad === 0) {
callback();
}
});
});
});
}
};
Edit: Thanks for Bergi's remind that there's the files are an array and you have to call the callback when all files is processed, so we have to further use a variable to decide how many files are not processed yet.
I'm learning node, its events API and trying to make a simple example.
So here's my code:
var fs = require('fs');
var util = require('util');
var events = require('events');
var FileLoader = function () {
events.EventEmitter.call(this);
this.load = function (url) {
fs.readFile(url, function (err, data) {
if (err) {
throw err;
} else {
this.emit('loaded', data.toString());
}
});
};
};
util.inherits(FileLoader, events.EventEmitter);
module.exports = FileLoader;
And I want to load() a text file and when it is loaded, catch it with .on('loaded',function(){...}), but this is undefined, so program crashes.
I'm definitely missing something, how to make it work?
This is not an issue with Node, it is an issue with JavaScript. The this in this.emit is not a FileLoader instance.
I recommend you read up on the behavior of this in JavaScript. The MDN docs for this may be helpful for you.
In this case, you need to bind the this inside your readFile callback so that the inner this is the outer this by adding .bind(this) to your callback.
this.load = function (url) {
fs.readFile(url, function (err, data) {
if (err) {
throw err;
} else {
this.emit('loaded', data.toString());
}
}.bind(this));
};