Javascript / node.js - callbacks inside Promise - node.js

i have some callbacks inside my promise:
var res = new Promise(resolve => {
console.log('trig1');
var out = fs.createWriteStream(pathToFile);
console.log('trig2');
out.on('finish', function() {
console.log('out finish')
})
out.on('close', function() {
console.log('out close')
})
out.on('error', function(err) {
console.log('out error: ' + err)
})
})
When i call this promise, it creates the file on the path and prints:
trig1
trig2
Nothing more. Why are my callbacks not executed?
Greetings and thanks

Because there was no error, you didn't write anything, and you didn't close this stream. That's why any of those events fired.
Try to change your code so it will do something.
var res = new Promise(resolve => {
console.log('trig1');
var out = fs.createWriteStream(pathToFile);
console.log('trig2');
out.on('finish', function() {
console.log('out finish')
})
out.on('close', function() {
console.log('out close')
})
out.on('error', function(err) {
console.log('out error: ' + err)
});
out.close(); // this will fire `close` event
})

Related

Nodejs triggers function before previous function finish

What I need to do is,
to check if fileFullPath exist
if not, in the end of successfull file download, to call saveInfo.
When I execute the application, what I observe is, it calls saveInfo before finishing file write operation. And I get error message:
(node:20224) UnhandledPromiseRejectionWarning: Error: BatchCluster has ended, cannot enqueue -charset
What am I doing wrong?
async function dl(url, path, data = null) {
await request.get({
url: url,
})
.on("error", async function (error) {
console.log(error);
return false;
})
.on('response', async function (res) {
var fileExt = res.headers['content-type'].split('/')[1];
var fileFullPath = `${path}.${fileExt}`;
await res.pipe(fs.createWriteStream(fileFullPath));
console.log("file downloaded");
if (data) {
await saveInfo(fileFullPath, data);
}
});
return true;
}
async function saveInfo(filePath, data) {
await exiftool.write(filePath, {
Keywords: data.keywords,
Copyright: data.copyright,
});
console.log("Tags are saved");
exiftool.end();
}
OK, I found a way to do this. piping to streams is not very friendly to promises so I ended up doing some manual promise manipulations. I think better promise support for streams is coming to node.js as we already have some async iterators. Anyway, here's a way to make things work by watching for the right events on your streams:
function dl(url, path, data = null) {
return new Promise((resolve, reject) => {
request.get({
url: url,
}).on("error", function (error) {
console.log(error);
reject(error);
}).on('response', function (res) {
let fileExt = res.headers['content-type'].split('/')[1];
let fileFullPath = `${path}.${fileExt}`;
let writeStream = fs.createWriteStream(fileFullPath);
// set up event handlers to monitor the writeStream for error or completion
writeStream.on('error', reject).on('close', async () => {
if (data) {
try {
await saveInfo(fileFullPath, data);
} catch(e) {
reject(e);
return;
}
}
console.log("file downloaded");
resolve(true);
});
// send the response stream to our file
res.pipe(writeStream).on('error', reject);
});
});
}
async function saveInfo(filePath, data) {
await exiftool.write(filePath, {
Keywords: data.keywords,
Copyright: data.copyright,
});
console.log("Tags are saved");
exiftool.end();
}

Make possible to get rejected value

I hope someone can help me out with my issue to get back the reject value out of this functions:
(async () => {
await queue.add(() => startCompare(orgpath, dpath, xray));
console.log('done compare ' + entry);
})();
This call the function:
async function startCompare(orgFile, compFile, xFile)
{
let gstderr;
return new Promise((resolve, reject) => {
spawn('compare', [orgFile, compFile, '-metric', 'AE', 'null:'])
.progress(function (childProcess) {
childProcess.stdout.on('data', function (data) {
console.log('[spawn] stdout: ', data.toString());
});
childProcess.stderr.on('data', function (data) {
gstderr = data.toString();
console.log('[spawn] stderr: ', data.toString());
});
}).then(res => {
resolve(true);
}).catch(error => {
resolve(gstderr);
});
});
}
My goal is how to get back gstderr value when rejected. Maybe the arrow function is the wrong way? I want to print out the value at: console.log('done compare ' + entry + xxxxx); where xxxxx is the rejected value.
You can catch the rejected value of a promise with async/await by using try/catch like so:
(async () => {
try {
// if it gets resolved lands here
const result = await startCompare(...args)
console.log(result) // should be true
} catch(error) {
// if it gets rejected it lands here
console.log(error) // should be the gstderr
}
})()
Hope it helps!
export const canLogin = createAsyncThunk("AUTH/login",
async (loginData ,{ rejectWithValue })=> {
try{
const response=await axios.post(AuthConfig.API_URL + AuthConfig.LOGIN_URI, loginData)
return response
}
catch(error){
if (!error.response || !error.message)
throw error
return rejectWithValue(getErrorMessage(error))
}
}
);
export function getErrorMessage(error) {
return (
error?.response?.data?.message ||
error?.response?.data.error ||
error?.response?.data ||
error?.message ||
error.toString()
);
}

node csv files upload synchronous import followed by mongoose find

I am not able to get results from a mongoose find query after successfully uploading csv files. The data from the csvs is being saved in my collection OK. But I am getting no results from a following find query. What causes promises to not conform to the sequence? I feel I may be close to understanding promises as they are handled in the event loop but this case escapes me?
let uploadfiles = function (req,res) {
return new Promise( function (resolve, reject) {
upload(req, res, (err) => {
promises = [];
if (!err) {
if (Object.keys(req.files).length>0) {
simpleLoop(req.files, function (value, key, lovey) {
promises.push(insertStreamData(req.files[key]["originalname"]));
});
console.log('hither');
}
}
return Promise.all(promises);
});
resolve('something');
});
};
// insert uploaded csv data from files into db
function insertStreamData(filename){
var originalFileName = filename;
var thefilename = originalFileName.split('.');
var csvfile = "./public/files/"+originalFileName;
var stream = fs.createReadStream(csvfile, { headers: true});
var csvStream = csv().on("data", function(data){
if(data[0] != 'Product #' && data[7] != 0){
var item = new Product({
'Invoice':thefilename[0],
'Product #': data[0],
'SKU': data[1],
'UpcCode': data[2],
'Description': data[3],
'MfgNo': data[4],
'Vendor': data[5],
'Order Qty': data[6],
'Ship Qty': data[7],
'Min Sell': data[8],
'Retail': data[9],
'Cost': data[10],
'Ext Cost': data[11],
'Box': data[12]
});
item.save(function(error){ if(error){ throw error; } });
}
}).on("end", function(){
console.log('the end');
});
stream.pipe(csvStream);
}
let getincomingproducts = function(){
return new Promise(function (resolve, reject) {
resolve('done');
});
};
router.post('/uploaddata', function(req, res) {
uploadfiles(req,res).then(function(result){
return getincomingproducts();
}).then(function(result){
console.log(result);
res.redirect('/showdata');
}).catch(err => console.log("Caught " + err));
});
Output 'done' is logged to the console prior to the first promise. I want to replace done with mongoose find results. But a simple console log demonstrates the unsynchronous result.
done
hither
the end
the end
the end
the end
Please don't mix Promise constructors and such with async and await. This could be a lot easier if you just use await and async functions. And only wrap the callback taking functions once.
A clean version wouldn't have any business logic in the Promise constructors and would used await rather than then in an async function.
I found the second point in the program where it doesn't wait for the completion of an action that is that the insertStreamData does not return a promise that is fulfilled when it is done which is what is causing the out of order issue. I will update my answer.
I see a issue in upload files where it has:
return Promise.all(promises);
});
resolve('something');
which should be
return Promise.all(promises).then(function () {
resolve('something');
});
This change should cause the output to be hither, the end * 4, done. If you want the insertStreamData to occur in serial rather than parallel, that requires a different change.
My version of the script:
function upload_wrapper(req, res) {
return new Promise(function(resolve, reject) {
upload(req, res, function (err) {
if (err) {
reject(err);
} else {
resolve();
}
});
})
}
let uploadfiles = async function(req, res) {
await upload_wrapper(req, res);
let promises = [];
if (Object.keys(req.files).length > 0) {
simpleLoop(req.files, function(value, key, lovey) {
promises.push(insertStreamData(req.files[key]["originalname"]));
});
console.log('hither');
}
await Promise.all(promises);
return 'something';
};
// insert uploaded csv data from files into db
function insertStreamData(filename) {
return new Promise(function(resolve, reject) {
var originalFileName = filename;
var thefilename = originalFileName.split('.');
var csvfile = "./public/files/" + originalFileName;
var stream = fs.createReadStream(csvfile, {
headers: true
});
var csvStream = csv();
csvStream.on("data", function(data) {
if (data[0] != 'Product #' && data[7] != 0) {
var item = new Product({
'Invoice': thefilename[0],
'Product #': data[0],
'SKU': data[1],
'UpcCode': data[2],
'Description': data[3],
'MfgNo': data[4],
'Vendor': data[5],
'Order Qty': data[6],
'Ship Qty': data[7],
'Min Sell': data[8],
'Retail': data[9],
'Cost': data[10],
'Ext Cost': data[11],
'Box': data[12]
});
item.save(function(error) {
if (error) {
//throw error;
csvStream.pause(); // here the stream should be destroyed if this stops at the first error
reject(error);
}
});
}
}).on("end", function() {
console.log('the end');
resolve('the end');
});
stream.pipe(csvStream);
});
}
let getincomingproducts = function() {
return Promise.resolve('done');
};
router.post('/uploaddata', async function(req, res) {
try {
let result = await uploadfiles(req, res);
let result = await getincomingproducts();
console.log(result);
res.redirect('/showdata');
} catch (err) {
console.log("Caught " + err)
}
});
Untested. It still could be improved.

Sinon mocking with Sequelize

I have the following nodejs function using Sequelize:
var processDatabase = function (dbConnection, schema, recordsets) {
var myLogTable = dbConnection.define(schema.tableName, schema.myLogSchema, schema.myLogSchemaIndex);
myLogTable.sync({
force: false,
freezeTableName: true,
logging: console.log
}).then(function () {
console.log('Table synced...');
for (k = 0; k < recordsets.length; k++) {
var query = "Some query";
dbConnection.query(
query, {
type: dbConnection.QueryTypes.SELECT
}
)
.then(function (results) {
console.log('MYSQL Selection Done');
})
.catch(function (err) {
console.log('MYSQL Error: ' + err.message);
});
}
}).catch(function (err) {
console.log('MYSQL Sync Error: ' + err.message);
});
};
I am new to mocking and do not especially know how to test the catch part.
This is my unit test which I can come up with, but I do not know how a call to sync can go to the catch part:
describe('when call processDatabase', function () {
it('should process successfully when sync fails', function (done) {
seqConnection.define = function (tableName, schema, schemaIndex) {
return mockMyLogModel;
};
processProfilesNotMapped(seqConnection, {
tableName: 'SomeTable',
myLogSchema: myLogSchema,
myLogSchemaIndex: myLogSchemaIndex
}, []);
done();
})
});
How would I write my mocking so that I can test both catch and also then so that they can be covered?
You need to defer an exception in your mock, since "sync" is using promises. You can use q library or any other. This way when you execute the sync function it will go to the catch section
Example using q:
describe('when call processDatabase', function () {
it('should process successfully when sync fails', function (done) {
seqConnection.define = function (tableName, schema, schemaIndex) {
const mock = {
sync: function(){
const deferred = q.defer();
deferred.reject(new Error('Some error'));
return deferred.promise;
}
}
return mock;
};
expect(
function(){
cmdManager.execute("getProfileDummy","hosar#gmail.com")
}
).to.throw(Error);
processProfilesNotMapped(seqConnection, {
tableName: 'SomeTable',
myLogSchema: myLogSchema,
myLogSchemaIndex: myLogSchemaIndex
}, []);
done();
})
});

node.js mariasql return undefined

I believe i have a problem with the Syntax.
By the Function xx the return is undefined :(.
Here the Problem in one File.
var Client = require('mariasql');
var inspect = require('util').inspect;
var c = new Client();
c.connect({
host: '127.0.0.1',
user: 'root',
password: '38nudel5nu',
db: 'artikel2'
});
var login = function(){
console.log("LOGIN\n");
c.on('connect', function() {
console.log('Client connected');
})
.on('error', function(err) {
console.log('Client error: ' + err);
})
.on('close', function(hadError) {
console.log('Client closed');
});
}
var end = function(){
console.log("EXIT");
c.end();
}
login();
var xx = function(){
c.query("SELECT COUNT(ArtikelID) AS Count FROM artikel")
.on('result', function(res) {
res.on('row', function(row) {
return "YOLO";
})
.on('error', function(err) {
})
.on('end', function(info) {
});
})
.on('end', function() {
});
}
var autohaus = xx();
console.log("\n\n --> " + autohaus);
And here is the Output:
[cseipel#myhost testumgebung]$ node skript.js LOGIN
--> undefined Client connected
You're using an asynchronous function as if it were synchronous. That's not going to work. You need to pass in a callback to your ArtikelCount function and call the callback once you have the results you want (the typical convention for callbacks is to have the first argument be an error if an error occurred, otherwise it should be null).
Example:
var ArtikelCount = function(cb) {
var count,
error;
c.query('SELECT COUNT(ArtikelID) AS Count FROM artikel')
.on('result', function(res) {
res.on('row', function(row) {
count = row.Count;
})
.on('error', function(err) {
console.log('Result error: ' + inspect(err));
error = err;
})
.on('end', function(info) {
console.log('Result finished successfully');
});
})
.on('end', function() {
console.log('Done with all results');
cb(error, count);
});
}
Then use it like:
wc.ArtikelCount(function(err, count) {
if (err)
throw err;
else
console.log('Row count', count);
});

Resources