node-celery asychronise, await client.call() finish it's inner function - node.js

I am using node-celery 0.2.8 in my node.js server,and I create, use await on client.on(), hoping the code will console log result first, then log 'finally'. But the code console log 'finally' first, then after go through all the code, console log result. Could anyone help me to let the await work on client.on()? I just hope the code can execute in order, and wait at the asychronise request.
async(ctx) => {
const client = celery.createClient({
CELERY_BROKER_URL: 'amqp://guest:guest#172.**.2.**:5672//',
CELERY_RESULT_BACKEND: 'redis://172.**.2.**:6379/2',
CELERY_ROUTES: {
'street.add_account_multi': {'queue': 'street_default'},
}
});
await client.on('connect', () => {
client.call('street.add_account_multi', [crawlers], function (result) {
console.log(result);
});
});
console.log('finally');
}

Please try to put your operation after 'connect' event into a promise and then await resolve.
let clientConnect = new Promise((resolve, reject) => {
client.on('connect',() => {
client.call('street.add_account_multi', [crawlers], function(result) {
console.log(result);
resolve(result);
});
})
});
await clientConnect;
console.log('finally');

Related

Function meant to send data to Redis server sends the data, but will not send success response

My setJWT function sends a key/value pair to my local Redis server. I've confirmed the data is set with the redis-cli tool. However, the console.log("setJWT success") and res.json(response); in the function aren't triggered.
Any idea what I'm doing wrong?
const redis = require('redis');
const client = redis.createClient(process.env.REDIS_URL);
client.connect();
client.on('connect', () => {
console.log('connected');
});
const setJWT = (key, value) => {
return new Promise((resolve, reject) => {
try {
console.log(key, value)
return client.set(key, value, (error, response) => {
if (error) reject(error);
resolve(response);
console.log("setJWT success");
res.json(response);
});
} catch (error) {
reject(error);
}
});
};
there are a lot of things that don't look correctly, firstly you are referencing the variable res, but It doesn't exist in your code, also it's not necessary to wrap everything in a promise, you can use an async/await function, after your resolve or reject a promise the code finish, for that reason never the res.json code is executed, because the promise finished before that line of code, in this link you can see an example of redis in more details.
Here's the new, working, version of the function:
const setJWT = (key, value) => {
try {
client.set(key, value);
console.log("setJWT success");
} catch (error) {
console.log(error);
}
};

NodeJS Promises do not return AWS Athena Error

I've written a web interface to allow users to query AWS Athena. It is using NodeJS Promises with Async and Await, but the error handling is not good. When a user enters an invalid SQL query (table not found), the response is always "{}". I've tried Athena-express and Athena-client. I then tried the Athena-client "toStream()" syntax.
var stream = client.execute('SELECT * from sometable limit 1').toStream()
stream.on('data', function(record) { console.log('record='+JSON.stringify(record)) })
stream.on('query_end', function(queryExecution) { console.log(queryExecution) })
stream.on('end', function() { console.log('end') })
stream.on('error', function(e) { console.error(e) })
And it returned an error. I thought great, let me encapsulate it in a promise. I've used:
new Promise((resolve, reject) => ...
require('stream/promises');
require('stream-promise');
Essentially, the moment I put it in a promise, the error I get back is always "{}". Does anyone know how you can get the actual Athena error returned using promises without it crashing the app?
let executeQuery = new Promise((resolve, reject) =>
{
let response = client.execute('SELECT * from sometable limit 1').toStream();
response.on('error', (err) => { reject(err); })
response.on('finish', function() { resolve(response); })
})
async function run()
{
try
{
let response = await executeQuery;
console.log('response='+JSON.stringify(response));
}
catch(e) { console.log('MY error='+JSON.stringify(e)) }
}
run();
It returns "My error={}", and I need it to return "Table sometable does not exist"
Found my issue. I can't JSON.stringify(e) I have to JSON.stringify(e.message)
const output = JSON.stringify(e.message);
That was annoying!

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();
}

ctx.body undefined in async/await function

I'm trying to return from my node server with koa to my angular front end the result of an api call. Here's my controller which require a npm module which provides access to their api. Await should wait for the result and than return, am I wrong? I did something similar in a previous project but I was asking data from a db.
Why it is not working?
const color = require('colourlovers');
exports.getAllColors = async (ctx) => {
ctx.res.body = await color.get('/color/FFFFFF', { format: 'json' }, (err, data) => {
console.log(data);//<---here is logging the data
return data;
});
console.log(ctx.res.body);//<---here is undefined
ctx.status=200;
};
You can not await color.get because it uses callbacks instead of promises (well, you can await it, but it doesn't do what you'd expect). So to use await, you need to build the promise yourself:
ctx.res.body = await new Promise((resolve, reject) => {
color.get('/color/FFFFFF', { format: 'json' }, (err, data) => {
if(err) reject(err);
else resolve(data);
});
});
Now it'll wait for the promise to be resolved or rejected.

Node js stop long process task when Promise reject it

I create a promise function to processing a long-time query task. Some time the task will block for hours. I want set a time out to stop the task. Below is the code.
It can return error message correctly, but it still running connection.execute() for long time before stop. So how can stop it immediately when it return reject message?
Thanks!
function executeQuery(connection, query) {
return new Promise((resolve, reject) => {
"use strict";
//long time query
connection.execute(query, function (err, results) {
if (err) reject('Error when fetch data');
else resolve(results);
clearTimeout(t);
});
let t = setTimeout(function () {
reject('Time Out');
}, 10);
})
(async () => {
"use strict";
oracle.outFormat = oracle.OBJECT;
try {
let query = fs.readFileSync("query.sql").toString();
let results = await executeQuery(connection, query);
console.log(results.rows);
} catch (e) {
console.log(`error:${e}`);
}
So how can stop it immediately when it return reject message?
According to the docs, you can use connection.break:
return new Promise((resolve, reject) => {
connection.execute(query, (err, results) => {
if (err) reject(err);
else resolve(results);
clearTimeout(t);
});
const t = setTimeout(() => {
connection.break(reject); // is supposed to call the execute callback with an error
}, 10);
})
Make sure to also release the connection in a finally block.
Try this (using bluebird promises):
var execute = Promise.promisify(connection.execute);
function executeQuery(connection, query) {
return execute.call(connection, query)
.timeout(10000)
.then(function (results) {
// handle results here
})
.catch(Promise.TimeoutError, function (err) {
// handle timeout error here
});
.catch(function (err) {
// handle other errors here
});
};
If this still blocks, there's a possibility that the database driver you are using is actually synchronous rather than asynchronous. In that case, that driver would be incompatible with the node event loop and you may want to look into another one.
As Bergi mentioned, you'll need to use the connection.break method.
Given the following function:
create or replace function wait_for_seconds(
p_seconds in number
)
return number
is
begin
dbms_lock.sleep(p_seconds);
return 1;
end;
Here's an example of its use:
const oracledb = require('oracledb');
const config = require('./dbConfig.js');
let conn;
let err;
let timeout;
oracledb.getConnection(config)
.then((c) => {
conn = c;
timeout = setTimeout(() => {
console.log('Timeout expired, invoking break');
conn.break((err) => {
console.log('Break finished', err);
});
}, 5000);
return conn.execute(
`select wait_for_seconds(10)
from dual`,
[],
{
outFormat: oracledb.OBJECT
}
);
})
.then(result => {
console.log(result.rows);
clearTimeout(timeout);
})
.catch(err => {
console.log('Error in processing', err);
if (/^Error: ORA-01013/.test(err)) {
console.log('The error was related to the timeout');
}
})
.then(() => {
if (conn) { // conn assignment worked, need to close
return conn.close();
}
})
.catch(err => {
console.log('Error during close', err)
});
Keep in mind that the setTimeout call is just before the execute (because of the return statement). That timeout will start counting down immediately. However, the execute call isn't guaranteed to start immediately as it uses a thread from the thread pool and it may have to wait till one is available. Just something to keep in mind...

Resources