In Azure Portal, I'm modifying the scripts of an Easy Table, and I'm having trouble figuring out how to properly return a response to a client within a modified script. This is using the node package azure-mobile-apps
Just as a simple example, let's say I had a table that I was modifying the 'insert' function on, like so:
function getUserHeader(context) {
return context.req.headers['user-id'];
}
table.insert(function (context) {
context.item._user_id = getUserHeader(context);
return context.execute();
});
Now this is all fine, works perfectly as expected, item on the table ends up with correct _user_id and everything.
But let's say, for whatever reason, I had to make getUserHeader asynchronous and return a promise (maybe I want to verify a token or check something on a related table before executing the context). Here's what the above code might look like async:
function getUserHeader(context) {
return new Promise(function(resolve, reject){
resolve(context.req.headers['user-id']);
});
}
table.insert(function (context) {
getUserHeader(context)
.then(function(uid) {
context.item._user_id = uid;
return context.execute();
})
});
Now it works on one level: context.execute does run, and the record does get created, with the correct _user_id. HOWEVER the http call the client made never gets a response. Normally the http response returns with the item that cot added to the table (in the case of insert), but not here.
What's the proper way to add in asychronous functions to the workflow of the easy table scripts?
You missed return keyword when you call a Promise function.
This would work if you change it to:
table.insert(function (context) {
return getUserHeader(context).then(function(uid) {
context.item._user_id = uid;
return context.execute();
})
});
Related
I'm still somewhat new to working with Node and def new to working asynchronously and with promises.
I have an application that is hitting a REST endpoint, then calling a chain of functions. The end of this chain is calling hgetall and I need to wait until I get the result and pass it back. I'm testing with Postman and I'm getting {} back instead of the id. I can console.log the id, so I know that this is because some of the code isn't waiting for the result of hgetall before continuing.
I'm using await to wait for the result of hgetall, but that's only working for the end of the chain. do I need to do this for the entire chain of functions, or is there a way to have everything wait for the result before continuing on? Here's the last bit of the logic chain:
Note: I've removed some of the logic from the below functions and renamed a few things to make it a bit easier to see the flow and whats going on with this particular issue. So, some of it may look a bit weird.
For this example, it will call GetProfileById().
FindProfile(info) {
var profile;
var profileId = this.GenerateProfileIdkey(info); // Yes, this will always give me the correct key
profile = this.GetProfileById(profileId);
return profile;
}
This checks with the Redis exists, to verify if the key exists, then tries to get the id with that key. I am now aware that the Key() returns true instead of what Redis actually returns, but I'll fix that once I get this current issue resolved.
GetProfileById(profileId) {
if ((this.datastore.Key(profileId) === true) && (profileId != null)) {
logger.info('GetProfileById ==> Profile found. Returning the profile');
return this.datastore.GetId(profileId);
} else {
logger.info(`GetProfileById ==> No profile found with key ${profileId}`)
return false;
}
}
GetId() then calls the data_store to get the id. This is also where I started to use await and async to try and wait for the result to come through before proceeding. This part does wait for the result, but the functions prior to this don't seem to wait for this one to return anything. Also curious why it only returns the key and not the value, but when I print out the result in hgetall I get the key and value?
async GetId(key) {
var result = await this.store.RedisGetId(key);
console.log('PDS ==> Here is the GetId result');
console.log(result); // returns [ 'id' ]
return result;
}
and finally, we have the hgetall call. Again, new to promises and async, so this may not be the best way of handling this or right at all, but it is getting the result and waiting for the result before it returns anything
async RedisGetId(key) {
var returnVal;
var values;
return new Promise((resolve, reject) => {
client.hgetall(key, (err, object) => {
if (err) {
reject(err);
} else {
resolve(Object.keys(object));
console.log(object); // returns {id: 'xxxxxxxxxxxxxx'}
return object;
}
});
});
}
Am I going to need to async every single function that could potentially end up making a Redis call, or is there a way to make the app wait for the Redis call to return something, then continue on?
Short answer is "Yes". In general, if a call makes an asynchronous request and you need to await the answer, you will need to do something to wait for it.
Sometimes, you can get smart and issue multiple calls at once and await all of them in parallel using Promise.all.
However, it looks like in your case your workflow is synchronous, so you will need to await each step individually. This can get ugly, so for redis I typically use something like promisify and make it easier to use native promises with redis. There is even an example on how to do this in the redis docs:
const {promisify} = require('util');
const getAsync = promisify(client.get).bind(client);
...
const fooVal = await getAsync('foo');
Makes your code much nicer.
I'm writing a script that is intended to load some stuff from .txt files and then perform multiple ( in a loop) requests to a website with node.js` browser emulator nightmare.
I have no problem with reading from the txt files and so no, but managing to make it run sync and without exceptions.
function visitPage(url, code) {
new Promise((resolve, reject) => {
Nightmare
.goto(url)
.click('.vote')
.insert('input[name=username]', 'testadmin')
.insert('.test-code-verify', code)
.click('.button.vote.submit')
.wait('.tag.vote.disabled,.validation-error')
.evaluate(() => document.querySelector('.validation -error').innerHTML)
.end()
.then(text => {
return text;
})
});
}
async function myBackEndLogic() {
try {
var br = 0, user, proxy, current, agent;
while(br < loops){
current = Math.floor(Math.random() * (maxLoops-br-1));
/*...getting user and so on..*/
const response = await visitPage('https://example.com/admin/login',"code")
br++;
}
} catch (error) {
console.error('ERROR:');
console.error(error);
}
}
myBackEndLogic();
The error that occurs is:
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'webContents' of undefined
So the questions are a few:
1) How to fix the exception
2) How to make it actually work sync and emulate everytime the address ( as in a previous attempt, which I didn't save, I fixed the exception, but the browser wasn't actually openning and it was basically skipped
3) (Not so important) Is it possible to select a few objects with
.wait('.class1,.class2,.validation-error')
and save each value in different variables or just get the text from the first that occured? ( if no any of these has occurred, then return 0 for example )
I see a few issues with the code above.
In the visitPage function, you are returning a Promise. That's fine, except you don't have to create the wrapping promise! It looks like nightmare returns a promise for you. Today, you're dropping an errors that promise returns by wrapping it. Instead - just use an async function!
async function visitPage(url, code) {
return Nightmare
.goto(url)
.click('.vote')
.insert('input[name=username]', 'testadmin')
.insert('.test-code-verify', code)
.click('.button.vote.submit')
.wait('.tag.vote.disabled,.validation-error')
.evaluate(() => document.querySelector('.validation -error').innerHTML)
.end();
}
You probably don't want to wrap the content of this method in a 'try/catch'. Just let the promises flow :)
async function myBackEndLogic() {
var br = 0, user, proxy, current, agent;
while(br < loops){
current = Math.floor(Math.random() * (maxLoops-br-1));
const response = await visitPage('https://example.com/admin/login',"code")
br++;
}
}
When you run your method - make sure to include a catch! Or a then! Otherwise, your app may exit early.
myBackEndLogic()
.then(() => console.log('donesies!'))
.catch(console.error);
I'm not sure if any of this will help with your specific issue, but hopefully it gets you on the right path :)
Please keep in mind that I am new to node.js and I am used with android development.
My scenario is like this:
Run a query against the database that returns either null or a value
Call a web service with that database value, that offers info paginated, meaning that on a call I get a parameter to pass for the next call if there is more info to fetch.
After all the items are retrieved, store them in a database table
If everything is well, for each item received previously, I need to make another web call and store the retrieved info in another table
if fetching any of the data set fails, all data must be reverted from the database
So far, I've tried this:
getAllData: function(){
self.getMainWebData(null)
.then(function(result){
//get secondary data for each result row and insert it into database
}
}
getMainWebData: function(nextPage){
return new Promise(function(resolve, reject) {
module.getWebData(nextPage, function(errorReturned, response, values) {
if (errorReturned) {
reject(errorReturned);
}
nextPage = response.nextPageValue;
resolve(values);
})
}).then(function(result) {
//here I need to insert the returned values in database
//there's a new page, so fetch the next set of data
if (nextPage) {
//call again getMainWebData?
self.getMainWebData(nextPage)
}
})
There are a few things missing, from what I've tested, getAllData.then fires only one for the first set of items and not for others, so clearly handling the returned data in not right.
LATER EDIT: I've edited the scenario. Given some more research my feeling is that I could use a chain or .then() to perform the operations in a sequence.
Yes it is happening as you are resolving the promise on the first call itself. You should put resolve(value) inside an if statement which checks if more data is needed to be fetched. You will also need to restructure the logic as node is asynchronous. And the above code will not work unless you do change the logic.
Solution 1:
You can either append the paginated response to another variable outside the context of the calls you are making. And later use that value after you are done with the response.
getAllData: function(){
self.getMainWebData(null)
.then(function(result){
// make your database transaction if result is not an error
}
}
function getList(nextpage, result, callback){
module.getWebData(nextPage, function(errorReturned, response, values) {
if(errorReturned)
callback(errorReturned);
result.push(values);
nextPage = response.nextPageValue;
if(nextPage)
getList(nextPage, result, callback);
else
callback(null, result);
})
}
getMainWebData: function(nextPage){
return new Promise(function(resolve, reject) {
var result = [];
getList(nextpage, result, function(err, results){
if(err)
reject(err);
else{
// Here all the items are retrieved, you can store them in a database table
// for each item received make your web call and store it into another variable or result set
// suggestion is to make the database transaction only after you have retrieved all your data
// other wise it will include database rollback which will depend on the database which you are using
// after all this is done resolve the promise with the returning value
resolve(results);
}
});
})
}
I have not tested it but something like this should work. If problem persists let me know in comments.
Solution 2:
You can remove promises and try the same thing with callback as they are easier to follow and will make sense to the programmers who are familiar with structural languages.
Looking at your problem, I have created a code that would loop through promises.
and would only procede if there is more data to be fetched, the stored data would still be available in an array.
I hope this help. Dont forget to mark if it helps.
let fetchData = (offset = 0, limit= 10) => {
let addresses = [...Array(100).keys()];
return Promise.resolve(addresses.slice(offset, offset + limit))
}
// o => offset & l => limit
let o = 0, l = 10;
let results = [];
let process = p => {
if (!p) return p;
return p.then(data => {
// Process with data here;
console.log(data);
// increment the pagination
o += l;
results = results.concat(data);
// while there is data equal to limit set then fetch next page
// otherwise return the collected result
return (data.length == l)? process(fetchAddress(o, l)).then(data => data) : results;
})
}
process(fetchAddress(o, l))
.then(data => {
// All the fetched data will be here
}).catch(err => {
// Handle Error here.
// All the retrieved data from database will be available in "results" array
});
if You want to do it more often I have also created a gist for reference.
If You dont want to use any global variable, and want to do it in very functional way. You can check this example. However it requires little more complication.
Okay so below is a snippet of my code where I have cut many unnecessary things and unrelated but I have left the part dealing with the question.
I am using callbacks while calling the functions needed to run the necessary queries. Since I have many queries like these below, I was wondering if thats the right way to ensure the wanted order for the queries to be executed. I know I could remove the functions and simply put them inside a serialize but its really ugly to repeat the same code so I put them in functions, to put it more clear here is my question.
Question: If I have many queries inside functions the correct way to ensure the get executed in the wanted order is with callbacks as I have done?, even in cases where you dont want to return anything e.g (when updating a row/table in the DB)
get_data(pel, function(results){
var cntl = results;
get_user(pel, function(results_from_user){
update_data(0, 0, function(cb_result){
//do some stuff
});
});
});
function get_data(dt, callback)
{
db.get(`SELECT * FROM my_table`, function(error, row) {
var data_to_return = [..];
return callback(data_to_return);
});
}
function update_data(vdr,dwe,callback)
{
db.run(`UPDATE my_table SET val1='${..}', val2 = '${..}'`);
//..
return callback("updated");
}
function get_user(ms, callback)
{
db.get(`SELECT id FROM my_table_2 WHERE id=${..};`, function(error, row) {
if(row == undefined) db.run(`INSERT INTO my_table_2 (id) VALUES (?)`,[0]);
//..
var id_to_return = [..];
return callback(id_to_return);
});
}
perhaps I should add my code is working as expected, I am just making sure I am not using a weird way.
I can ensure you that you have made a typical solution. in fact callback are used to wait for the response before moving on to the next statement.Goog job
I’m working with the new office.js. I’m using the Excel.run functionality that returns a promise. I have a question about the promises pattern implemented by the library.
The samples all show this pattern
Excel.run( function (ctx) {
//set up something
return ctx.sync().then (function () {
//call another function somewhere to chain operations
});
}).then ( function () {
//do something else if you want
}).catch (function (error) {
handle errors
});
The problem is the ctx.sync().then() contained within Excel.run()
The way it is presented, you can’t chain promises in accordance with the promises spec because you lose the context object if you try and handle the then() outside of Excel.run()
So, the pattern seems to be promoting nested function calls, which is what promises are supposed to eliminate.
What I want to do is sequence several calls together through chaining like this:
Excel.run( function (ctx) {
return ctx.sync();
}).then ( function (ctx) {
return ctx.sync();
}).then ( function (ctx) {
return ctx.sync();
}).then ( function (ctx) {
return ctx.sync();
}).catch (function (error) {
});
Is this possible?
In general, the purpose of Excel.run is for a sequential operation against the OM with automatic cleanup at the end. That is, Excel.run creates a context, runs you operation, and then cleans up any host objects that were allocated.
That being said, as mentioned by Gab Royer, you can pass objects out. And moreover, each Excel object has a back-pointer to its "context" via the ".context" property. So for example, you can do this:
Excel.run(function (ctx) {
var worksheet = ctx.workbook.worksheets.getActiveWorksheet();
return ctx.sync(worksheet);
}).then(function(worksheet) {
worksheet.name = "Test"
return worksheet.context.sync();
}).catch(function(e) {
console.log(e)
});
As you can see, in the code above, you had created the worksheet object inside the Excel.run, but are using it outside.
If you have something like a Range object, it gets a little trickier. Ranges, unlike Worksheets, do not have persistent IDs (How could they? There is essentially a countless number of permutations of all possible combinations of cells). Instead, during Excel.run, we automatically create persistent pointers to the backing Range objects that get adjusted and kept-track-of by Excel. When the batch inside of Excel.run completes, we tell the host to destroy these references. So if you had code like this:
Excel.run(function (ctx) {
var range = ctx.workbook.getSelectedRange();
return ctx.sync(range);
}).then(function(range) {
range.format.fill.color = "red";
return ctx.sync();
}).catch(function(e) {
console.log(e)
})
It would run into an "InvalidObjectPath" error.
However, you can opt out of the tracked-object cleanup by manually adding the object to the ctx.trackedObjects collection. In doing this, however, you are taking it upon yourself to clean up at the end -- and you need to be extra careful, to remember to cleanup on not only on success, but on failure. Otherwise, you're essentially creating a memory leak that will keep slowing down the Excel host application.
var range;
Excel.run(function (ctx) {
range = ctx.workbook.getSelectedRange();
ctx.trackedObjects.add(range);
return ctx.sync(range);
}).then(function(range) {
range.format.fill.color = "red";
return range.context.sync();
}).then(function() {
// Attempt to clean up any orphaned references
range.context.trackedObjects.remove(range);
range.context.sync(); // don't need to await it, since it's just the final cleanup call
}).catch(function(e) {
console.log(e);
})
Long story short: it is certainly doable, and you can use objects after Excel.run. You'll just need to be responsible for memory-management for any objects that require "tracking". In the example above, there is no reason to go through this effort, since you could just as well have had the same code inside of Excel.run (remember, you can chain promises within the batch inside of Excel.run, too -- no need to do this on the outside). But if you have a scenario, where, say, you have a timer job that needs to run every so often (e.g., to update a stock ticker), or you want to create a button with an onclick handler for a particular object, etc. the technique above will let you create the objects inside of Excel.run, and then use them outside of it.
PS: With regards to the pattern requiring nesting: It is true that if you need to chain ctx.sync() calls within Excel.run, you will end up with a layer of nesting -- but just a single extra layer. Within, you still would still be able to chain your promises without the callback pyramid. E.g.,:
Excel.run(function (ctx) {
var range = ctx.workbook.worksheets.getActiveWorksheet().getRange("A1:C3");
range.load("values");
return ctx.sync()
.then(function () {
// Some set of actions against the OM, now that the "values"
// property has been loaded and can be read from the "range" object.
})
.then(ctx.sync)
.then(function () {
// Another set of actions against the OM, presumably after doing
// another load-requiring operation (otherwise could have
// been part of the same .then as above)
})
.then(ctx.sync)
.then(function() {
// One final set of actions
});
}).catch(function(error) {
console.log("Error: " + error);
});
While this would be possible since Excel.RequestContext.sync takes in a pass-through value, the goal of Excel.run is to manage trackedObjects for the function that it gets passed in. In promises chained after Excel.Run you would have to manage trackedObjects yourself, hence defeating the purpose of Excel.Run.
I'd suggest either declaring your function outside the Excel.Run if you don't like the added indentation, or creating your own RequestContext object.