autobahn JS, what if RPC's callee is async? - javascript

In the documentation of autobahnJS is provided the following example to illustrate how to make setup a remote procedure call (RPC):
...
// 3) register a procedure for remoting
function add2(args) {
return args[0] + args[1];
}
session.register('com.myapp.add2', add2);
// 4) call a remote procedure
session.call('com.myapp.add2', [2, 3]).then(
function (res) {
console.log("Result:", res);
}
);
...
What if add2 needs to do some async operation? My idea was that maybe one can could call back another remote function registered in the client that triggered the initial call to backend.add2. Something like this:
...
//backend code
function add2(args) {
setTimeout(function() {
console.log("We are done here");
session.call('client.added', [123])
}, 1000);
return null; // useless, this value is never used
}
session.register('backend.add2', add2);
// client code
session.call('backend.add2', [2, 3]).then(
function (res) {
console.log("Result:", res);
}
);
...
Do you see any better option? This seems a bit cumbersome to me. Ideally add2 would return a promise. But I am not sure whether this is possible over a RPC?

You can return a promise which is then resolved once the async function returns.
From the AutobahnJS API reference page:
function myAsyncFunction(args, kwargs, details) {
var d = new autobahn.when.defer();
setTimeout(function() {
d.resolve("async finished");
}, 1000);
return d.promise;
}

My example of registering async function
session.register('com.forlunch.list_chats', function (args, kwargs, details) {
return functions.list_chats(args);
})
which make query to the mysql database
async function list_chats(params){
var query = "SELECT * WHERE ... ;"
let res = await mysql_query(query)
return res
}
function mysql_query (query){
return new Promise(function(resolve, reject) {
const con = mysql.createConnection(mysql_options);
con.query(query,[], function (err, rows, fields) {
if (err) return reject(err);
resolve(rows);
});
})
}

Related

Issues with Async/Await during SOAP API call Javascript

Hopefully someone can point me to the right direction. I read up on waiting for functions to complete before continuing and I resolved myself to using await/async but I am just stuck now.
I tried to get the Async/Await process to work, tried to inject the await in various locations, with adjusting the functions to be async, but i can not get the PSA_Resultbody to return to the original request. Any pointers would be appreciated.
Thank you,
CE
PSA_Resultbody = ProcessPSAAPI(xmlpackage, PSA_Action);
console.log("3 - Returned data:" + PSA_Resultbody);
calls the below:
async function ProcessPSAAPI(xmlpackage, PSA_Action) { //psa action is part of the options
var options = {...};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log('0 - Start '+body.toString());
if(res.statusCode != 200) {
PSA_Resultcode = "Error: " +res.statusCode +" - "+ res.statusMessage;
} else {
PSA_Resultcode = "Success: " +res.statusCode +" - "+ res.statusMessage;
PSA_Resultbody = ParseResults(body.toString()); //parse the results for later use --SCRIPT NEEDS TO WAIT FOR RESULTBODY TO COMPLETE
console.log("1 -PSA_Resultbody as part of RES = "+PSA_Resultbody);
}
});
res.on("error", function (error) {
console.error(error);
PSA_Resultcode = res.statusCode +" - "+ res.statusMessage;
});
});
console.log('2 -RESULT BODY BEFORE SENDING BACK TO INITIATING FUNCTION: '+PSA_Resultbody);
req.write(xmlpackage);
req.end();
return PSA_Resultbody;
Based on the above, my console log order is: 3,2,0,1 in stead of 0,1,2,3.
0 and 1 will have the correct data, so the API Call does work, but 2 will be "undefined" and should have the same data that is in 1.
There's no way to await an event emitter, so using async in this case isn't going to be useful. You cannot "return" from inside an event either.
The solution here is to return a new custom promise and to use resolve() inside of the "end" event of your emitter.
It will look something like this:
function ProcessPSAAPI(xmlpackage, PSA_Action) {
return new Promise( (resolve, reject) => {
// other code
res.on("end", function (chunk) {
// other code
resolve(PSA_Resultbody);
});
res.on("error", function (error) {
// other code
reject(error);
});
});
}
Here's a quick tutorial on creating your own promises, which I've written to simplify comprehension of the subject (official docs are somewhat dry and complex imho).
I did not change your code. I just put the appropriate promise structure in to get you started. This should really be a lesson in promises. async await is a shorthand promise structure. A Promise is one way you wait on code. It can be thought of as an array of callbacks that will be executed when the Promise is resolved.
A simple promise works like this:
const myPromise = new Promise(function(resolve, reject) {
/* Your logic goes in here. It can be anything.
* But the important part to remember is that when you have success, resolve it.
* When you have a failure, reject it.
*/
someCallBackPattern(function(error, data) {
if(error) {
reject(error);
} else {
resolve(data);
}
});
});
// To get the data out you use 'then', and 'catch'. then has two arguments.
myPromise.then(function(data) {
// The first argument is the result from resolve.
}, function(err) {
// The second argument is the result from reject.
}).catch((err) => {
// you can also get to the error from the catch callback
});
This is kinda messy and complex. So there is async await.
async function() {
try {
const result = await myFunctionThatReturnsAPromise();
// result is the resolved data
} catch (err) {
// err is the rejected Error
}
}
function myFunctionThatReturnsAPromise() {
return new Promise((resolve, reject) => {
// your code
})
}
And thats how it works.
async function someFunction () { // You can not wait on results unless you are in an await function
PSA_Resultbody = await ProcessPSAAPI(xmlpackage, PSA_Action); // await on your results.
console.log("3 - Returned data:" + PSA_Resultbody);
}
function ProcessPSAAPI(xmlpackage, PSA_Action) { // This does not need to be async. Unless you are awaiting in it.
return new Promise((resolve, reject) => { // async await is a shorthand promise structure. Although you do not need to use promises. It really helps to get the structure correct.
var options = {...};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log('0 - Start '+body.toString());
if(res.statusCode != 200) {
PSA_Resultcode = "Error: " +res.statusCode +" - "+ res.statusMessage;
reject(new Error(PSA_Resultcode)); // Reject you errors
} else {
PSA_Resultcode = "Success: " +res.statusCode +" - "+ res.statusMessage;
PSA_Resultbody = ParseResults(body.toString()); //parse the results for later use --SCRIPT NEEDS TO WAIT FOR RESULTBODY TO COMPLETE
console.log("1 -PSA_Resultbody as part of RES = "+PSA_Resultbody);
resolve(PSA_Resultbody); // Resolve your result
}
});
res.on("error", function (error) {
console.error(error);
PSA_Resultcode = res.statusCode +" - "+ res.statusMessage;
reject(new Error(PSA_Resultcode)); // Reject you errors
});
});
console.log('2 -RESULT BODY BEFORE SENDING BACK TO INITIATING FUNCTION: '+PSA_Resultbody);
req.write(xmlpackage);
req.end();
})
}

How do I make a nested loop continue only after a asynchronous function has been resolved or how do I extend ".then" beyond the scope

I tried to prevent async problems with promises in the following code. By using a .then function everything within that function gets called after the function has been resolved. But now I have the problem that neither can I extend the scope of the ".then function" enough to include the bits after the second loop nor can I to my knowledge easily pause the code until the function has been properly resolved and THEN continue with the loop iteration.
Here's my main code(simplified):
let total = []
$.each(element, function(data) {
//Some other code
let out;
$.each(element2, function(data2) {
getZip(data2).then(function(txt){ //after everything has finished this get's called
out = someFunction(txt,data2);
total.push(out);
});
)};
console.log(total)//this gets called first
//some other code that does some stuff with total
)};
Here's the getZip code which is asynchronous:
function getZip(zipFile) {
return new Promise(function (resolve, reject){
zip = new JSZip()
JSZipUtils.getBinaryContent("someURL/" + zipFile, function (err, data) {
if (err) {
reject(err)
}
JSZip.loadAsync(data).then(function (zip) {
return zip.file(zipFile.replace(".zip", "")).async("text"); //gets the file within the zip andoutputs as text
}).then(function (txt) {
resolve(txt)
});
});
});
}
I'd be happy if either the getZip code could be made synchronous or if the before mentioned could be done.
I do not think I fully understand the code you have written. However, I recommend you use Promise.all. Here is an example I have written that I hope helps guide you:
let total = [];
$.each([1,2,3,4], function (data) {
// Some other code.
let out;
// Create a new promise so that we can wait on the getZip method.
new Promise(function (resolve, reject) {
// Create a holder variable. This variable with hold all the promises that are output from the getZip method you have.
let gZipPromises = [];
$.each([5,6,7,8], function (data2) {
// Your getZip method would go here. wrap the call to getZip in gZipPromises.push to push all the returned promises onto the holding variable.
gZipPromises.push(new Promise(function (resolve2, reject2) {
// Sample Code
setTimeout(function () {
total.push(data2);
resolve2("");
}, 10);
// End Sample Code.
}));
});
// Pass the holding variable to Promise.all so that all promises in the holding variable are executed before resolving.
Promise.all(gZipPromises).then(function() {
resolve()
});
}).then(function () {
// This will be called only when all getZip promises are completed in the second loop.
console.log(total);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
With that said, I could not test your code. But I think this would work:
(Please note that based on the code you provided, the variable total would be logged for each iteration of the top most $.each
let total = []
$.each(element, function(data) {
//Some other code
let out;
// Define a new promise.
new Promise(function (resolve, reject) {
let gZipPromises = [];
$.each(element2, function(data2) {
gZipPromises.push(
getZip(data2).then(function(txt){ //after everything has finished this get's called
out = someFunction(txt,data2);
total.push(out);
});
);
)};
Promise.all(gZipPromises).then(function() {
resolve()
});
}).then(function () {
console.log(total)
});
)};
const elements = [["foo.zip"],["bar.zip"],["baz.zip"]];
const totalOut = getAllZips(elements)
.then(text => console.info(text))
.catch(error => console.error(error))
function someFunction(text, data) {
return `${text}\nLength: ${data.length}`;
}
async function getAllZips(elements) {
let promises = [];
for(const element of elements) {
for(const data of element) {
promises.push(getZip(data).then(text => {
return someFunction(text, data);
}));
}
}
return Promise.all(promises);
}
async function getZip(file) {
return new Promise((resolve, reject) => {
JSZipUtils.getBinaryContent(`someURL/${file}`, async (err, data) => {
try {
if (err) throw err;
const zip = await JSZip.loadAsync(data);
const name = file.replace(".zip", "");
resolve(await zip.file(name).async('text'));
} catch(error) {
reject(error);
}
});
});
}
<script>/*IGNORE*/const JSZipUtils = {getBinaryContent:(p,c)=>errs.gbc?c(new Error('gbc'),null):c(null,{foo:true})};const JSZip = {loadAsync:(d)=>errs.la?Promise.reject(new Error('la')):({file:n=>({async:a=>errs.a?Promise.reject(new Error('a')):Promise.resolve('Hello World')})})};const errs = {gbc:false,la:false,a:false};/*IGNORE*/</script>
This kind of sounds like a use case for async iterator generators, but maybe I'm just over-engineering. You have a bunch of resources that you want to iterate over and their contents are asynchronous. You want it to "look" synchronous, so you can leverage async/await:
function getZip(zipFile) {
/*
* Theres no point in simplifying this function since it looks like
* the JSZip API deals with callbacks and not Promises.
*/
return Promise.resolve(zipFile);
}
function someFn(a, b) {
return `${a}: ${b.length}`;
}
async function* zipper(elements) {
for (const element of elements) {
for (const data of element) {
const txt = await getZip(data);
yield someFn(txt, data);
}
}
}
(async() => {
const elements = [
["hello"],
["world"],
["foo"],
["bar"]
];
let total = [];
for await (const out of zipper(elements)) {
total.push(out);
}
console.log(total);
})();

Pattern for dynamic Javascript promises

Inside a promise, I need to call and process an indeterminate number of asynch API responses after individually calling them either inside another promise, or after said promise, but before another so the order of execution is respected.
var promiseA = function() {
return new Promise(function(resolve, reject) {
// 1. Establish objects needed from one API endpoint
// 2. Call API endpoint for each object and parse
// 3. Only then continue to next promise
}
}
var finalPromise = function() {
return new Promise(function(resolve, reject) {
//
}
}
promiseA()
.then(finalPromise)
So inside promiseA, I find out how many objects I'll need to poll individually from an API. Each request is of course asynchronous. I need to make these calls and process the response before the final promise is called.
I am struggling to determine a pattern for this with promises, where I can dynamically create these promises and only allow the final promise to execute after the indeterminate and asynchronous have executed and processed. I've worked with other languages where this is possible, but I'm struggling to see it here with Promises.
Any help is appreciated.
I have changed the answer to incorporate the comments below. Since, you mentioned ES6 promises I shall stick to that. There are two basic types of callbacks that we might care about.
DOM load or other one time event callbacks (window.onload and so on)
Async method callback (AJAX call, setTimout and so on)
Since,
1.DOM load or other one time event
var p = new Promise(function(res, rej) {
window.onload = res();
};
2.Plain callback: these are callbacks that don't conform to a convention. e.g. setTimeout
var p = new Promise(function(res, rej){
setTimeout(function() {
//your business/view logic
success? res():rej(); //if successful resolve else reject
}, 2000);
});
In each of the above case the promise (var p) can be wrapped to be returned by a function.
var myAsyncMethod = function () {
var p = new ... // as mentioned in 1 or 2
return p;
}
Then the usage:
myAsyncMethod()
.then(function(){/* success-handler */})
.catch(function(/* failure-handler */));
Specific to your question you may have many such methods:
function baseAJAXCall (url) {
new Promise(functoin(rej, res) {
$.get(url, function(err, data){
if(err) {
rej();
}
else {
resolve(data);
}
});
}
};
function callAPIEndpoint(url) {
return baseAJAXCall(url);
}
function finalPromiseHandler () {
//your final business/view logic
}
//USAGE
callAPIEndpoint('/my-first-call')
.then(function(data){
var promiseArray = data.map(function(item){
return baseAJAXCall(item.url);
});
return Promise.all(promiseArray);
})
.then(finalPromiseHandler)
.catch(function(){
console.log('.error-message.');
});
Ref:
How do I convert an existing callback API to promises?.
http://www.datchley.name/es6-promises/
Links from comments below.
---OLD ANSWER: PLEASE OVERLOOK---
I am familiar with this library : https://github.com/kriskowal/q. And, you can do this using using the q.all and q.allSettled constructs. May be that is what you are looking for.
Normally, the pattern is to create a function that returns a promise.
function someAsyncFuncName1(url) {
var def = q.defer();
//async function
$.get(url, function(err, data){ //suppose
if(err){
def.reject();
}
else {
def.resolve(data); //pass the data to the .then() handler.
}
});
return def.promise;
}
function someAsyncFuncName2() {
var def = q.defer();
//async function
setTimeout(function(){ //suppose
//do something
if(good) {
def.resolve();
} else {
def.reject();
}
}, 1000); //arbitrary timeout of 1 second
return def.promise;
}
USAGE:
q.all([someAsyncFuncName1('/api-1'), someAsyncFuncName2()])
.then(function() {
//final handler
});
On a similar line of thought one can use q.allSettled() if you want to wait for all promises to return.
Hope this helps.
---EOF OLD ANSWER---
First of all, if async functions used in PromiseA don't return promises, you need to promisify them. You can do that with Promise constructor, but it's much better to use libraries, such as bluebird with their promisify methods.
Let's imagine, that we have two functions getUserIdsAsync and getUserAsync. The first on returns a list of user ids, getUserAsync returns an user data by userId. And you need to get a list of users by their ids. The code of PromiseA could look so:
var promiseA = function() {
return getUserIdsAsync()
.then(userIds => {
let ops = users.map(uid => getUserAsync(uid));
return Promise.all(ops);
});
}
The following snippet shows a solution without using any external library like bluebird. It follows the code snippet in your question (which seems to be more complicate than needed).
You have to collect all api promisses in an array. Then you can call Promise.all() to get a Promise for the end of all api promisses. Then you can do some final stuff, like parsing the result of each promise and continue afterwards.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var apiEndpoint = function (name) {
return new Promise( (resolve, reject) => {
setTimeout(() => resolve('API ' + name + ' job done'), 1000);
});
}
var promiseA = function() {
return new Promise( (resolve, reject) => {
const promisses = [];
for (var i=1; i < getRandomInt(3,6); i++) {
// 1. Establish objects needed from one API endpoint
promisses.push(apiEndpoint('This is number ' + i));
}
Promise.all(promisses).then( results => {
// do final stuff
for (const s of results) {
// 2. Call API endpoint for each object and parse
console.log(s);
}
// continue ...
// 3. Only then continue to next promise
resolve('now it is finished');
}).catch( err => reject(err) );
});
}
var finalPromise = function() {
return new Promise( (resolve, reject) => {
console.log('finalPromise');
resolve();
});
}
promiseA()
.then( () => finalPromise())
.catch(err => console.log(err) );
Please hold in mind that this solution is not easy to read. Using external libraries or reducing promisses can improve readability. Maybe you should take a look to the async/await pattern to get a much more better (readable) solution.
Here is a solution with async/await:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const apiEndpoint = function (name) {
return new Promise( (resolve, reject) => {
setTimeout(() => resolve('API ' + name + ' job done'), 1000);
});
}
async function promiseParallel () {
const promisses = [];
for (let i = 1; i < getRandomInt(3,6); i++) {
promisses.push(apiEndpoint('This is number ' + i));
}
for (const p of promisses) {
const x = await p;
console.log(x);
}
return ('everything is done');
}
promiseParallel().then( result => {
console.log(result);
}).catch( err => console.log(err) );
If you want call the promisses sequentially you can replace with:
async function promiseSequ () {
for (let i = 1; i < getRandomInt(3,6); i++) {
const x = await apiEndpoint('This is number ' + i);
console.log(x);
}
return ('everything is done');
}

Callback problems

I am new into javascript, and currently I'm trying to learning callback to my script. This script should return reduced words in array of objects
var fs = require('fs')
var dict = ['corpus.txt','corpus1.txt','corpus2.txt'];
mapping(dict, function(error,data){
if(error) throw error
console.log(data)
})
function mapping(list, callback){
var txtObj = []
list.forEach(function (val) {
readFile(val, function(error,data){
txtObj.push(data)
})
})
function readFile(src, cb){
fs.readFile(src,'utf8', function (error,data) {
if (error) return callback(error,null)
return mapred(data)
})
}
return callback(null,txtObj)
}
But it returns empty array. Any help would be appreciated.
Thanks!
`fs.readFile`
is an asynchronous function, before it's done and result callback is invoked, you are returning the empty txtObj array.
how to fix it ?
call return callback(null,txtObj) after fs.readFile is finished running.
and also, as you are running asynchronous function on an array of items one-by-one, it might not still work the way you want. might want to use modudles like async in nodejs
Here comes an asynchronous version using module async. synchronous file operation is strongly objected :)
var fs = require('fs')
var dict = ['corpus.txt','corpus1.txt','corpus2.txt'];
mapping(dict, function(error,data){
if(error) throw error
console.log(data)
})
function mapping(list, callback){
var txtObj = [],
async = require('async');
async.each(list, readFile, function(err) {
callback(err,txtObj)
});
function readFile(src, cb) {
fs.readFile(src,'utf8', function (error,data) {
if (error) {
cb(error);
}
else {
txtObj.push(mapred(data));
cb(null);
}
})
}
}
EDIT : You can do this without async, but it is little bit dirty isn't it ? also its OK if you remove the self invoking function inside the forEach, i included so that you can access the val, even after the callback is done
var fs = require('fs')
var dict = ['corpus.txt','corpus1.txt','corpus2.txt'];
mapping(dict, function(error,data){
if(error) throw error
console.log(data)
})
function mapping(list, callback){
var txtObj = [],
counter = list.length,
start = 0;
list.forEach(function (val) {
(function(val)
readFile(val, function(error,data) {
txtObj.push(data);
start++;
if(error || (start === counter)) {
callback(error,txtObj);
}
}))(val);
})
function readFile(src, cb) {
fs.readFile(src,'utf8', function (error,data) {
if (error) {
cb(error);
}
else {
txtObj.push(mapred(data));
cb(null);
}
})
}
}
The reason you are getting an empty array result is that you are performing the callback before the readFile function has a chance to populate the array. You are performing multiple asynchronous actions but not letting them to complete before continuing.
If there was only one async action, you would call callback() in the callback function of readFile, but as you need to perform multiple async actions before calling callback(), you should consider using fs.readFileSync().
Sometimes sync cannot be avoided.
function mapping(list, callback)
{
var txtObj = []
list.forEach(function(val)
{
try { txtObj.push(mapred(fs.readFileSync(val, 'utf8'))) }
catch(err) { callback(err) }
})
callback(null, txtObj)
}

error while using the Q library nodeJS

I have the following nodeJS code.I need to get the machines for each service from the redis database. I am using the 'q' library to simplify the callback problem. However I do not get the output.
I am new to node/callbacks/q. Where is my mistake in the code?
I have a controller.js file with the following code
function getMachines(services) {
var machines = Q.fcall(function() {});
services.forEach(function(service) {
var value = function() {
var deferred = Q.defer();
redisDB.readfromRedis(service, function(result) {
deferred.resolve(result);
});
return deferred.promise;
}
});
return machines;
}
testController.js(calling the getMachines function from the controller.js )
var services = ['dashDb22', 'service1', 'service2', 'service3']
var output = controller.getMachines(services)
console.log(output);
RedisDb.js
function readfromRedis(key, callback) {
client.smembers(key, function(error, value) {
if (error) {
throw error;
}
console.log('VALUE IS: = ' + value);
callback(value);
});
}
Your getMachines() doesn't do much, machines is useless and inside your forEach(), you're storing a function you never execute. Your code being simple, you don't really need to use Q, nodejs has a native Promise support.
function getMachines(services) {
// create an array of promises
var myPromises = services.map(function (service) {
// for each service, create a Promise
return new Promise(function (resolve, reject) {
redisDB.readfromRedis(service, function (result) {
resolve(result);
});
});
})
// takes an array of promises and returns a promise for when they've all
// fulfilled (completed successfully) with the values as the result
return Promise.all(myPromises);
}
getMachines(services).then(function (machines) {
// use machines here
});
You could also make readfromRedis() a promise to make it simpler to use.

Categories