"await" like similar function in JavaScript? - javascript

I'm having the function which is wait for sync after that it will load the content. Below function is working perfectly in firefox but not working in IE11
//Working in other browser and inserting the multiple records but not in IE
async function setup()
{
await Word.run(async(context)=> {
for (var i=0; i < 5; i++)
{
var controler = context.document.contentControls.getByTag("myTag"+i);
controler.load();
await context.sync();
controler.items[0].insertPargraph("Adding paragraph "+i);
}
}
)};
}
For IE11, below function is working perfectly for inserting only one record
//Working in IE for the only one record
function setUp()
{
Word.run(function (context){
var selectedTag = context.document.contentControls.getByTag("myTag");
context.load(selectedTag,'text');
return context.sync().then(function()
{
controler.items[0].insertPargraph("Adding paragraph 0")
});
})
}
Now problem is I want to iterate the loop for the contents, I have written the return function inside the forloop that the reason it is not working
//Below function is not working
function setUp()
{
Word.run(function (context){
for (var i=0; i < 5; i++)
{
var selectedTag = context.document.contentControls.getByTag("myTag");
context.load(selectedTag,'text');
return context.sync().then(function()
{
controler.items[0].insertPargraph("Adding paragraph 0")
});
}
})
}
How to write the await function for IE11 browsers. I have tried the goto Lable function but that also not working.

Your async version uses i with getTag and when adding the paragraph, but your subsequent code example doesn't. It matters for the solution.
Common Ground
You can create a promise chain, similar to my answer here but different enough it might be hard to apply that to your case. Basically, you start with a resolved promise (p), then use p = p.then(...) to build the chain.
If you don't need to use i's value
...then you can do it like this:
function setUp()
{
Word.run(function (context){
var p = Promise.resolve();
for (var i = 0; i < 5; i++)
{
p = p.then(function() {
var selectedTag = context.document.contentControls.getByTag("myTag");
context.load(selectedTag,'text');
return context.sync().then(function()
{
controler.items[0].insertPargraph("Adding paragraph 0")
});
});
}
})
}
If you do need to use i's value
...then we need to bake it into the code since you have to use var (IE11 has let, but it doesn't have ES2015 semantics for for loops):
function setUp()
{
Word.run(function (context){
function doOne(index) {
// We use `index` below
var selectedTag = context.document.contentControls.getByTag("myTag" + index);
context.load(selectedTag,'text');
return context.sync().then(function()
{
controler.items[0].insertPargraph("Adding paragraph " + index)
});
}
var p = Promise.resolve();
for (var i = 0; i < 5; i++)
{
p = p.then(doOne.bind(null, i));
}
})
}
Giving setUp a return value
Your async version assumes that Word.run returns a promise and that it expects its callback to return a promise. I can't find any documentation to support that, but then, the web documentation for this stuff appears to be truly amazingly bad.
If both of those assumptions are true, then to haev setUp return a promise, we'd only need to make small changes: return before Word.run and return p; at the end of the callback (see *** comments);
function setUp()
{
return Word.run(function (context){ // ***
function doOne(index) {
// We use `index` below
var selectedTag = context.document.contentControls.getByTag("myTag" + index);
context.load(selectedTag,'text');
return context.sync().then(function()
{
controler.items[0].insertPargraph("Adding paragraph " + index)
});
}
var p = Promise.resolve();
for (var i = 0; i < 5; i++)
{
p = p.then(doOne.bind(null, i));
}
return p; // ***
})
}
But if Word.run doesn't return a promise, or doesn't expect a promise from its callback, that won't work and we have to create our own:
function setUp()
{
return new Promise(function(resolve, reject) { // ***
Word.run(function (context) {
function doOne(index) {
// We use `index` below
var selectedTag = context.document.contentControls.getByTag("myTag" + index);
context.load(selectedTag,'text');
return context.sync().then(function()
{
controler.items[0].insertPargraph("Adding paragraph " + index)
});
}
var p = Promise.resolve();
for (var i = 0; i < 5; i++)
{
p = p.then(doOne.bind(null, i));
}
p.then(resolve).catch(reject); // ***
})
});
}

I think what you are trying to achieve is to chain the sync() calls together so that the callback in Word.run only resolves when everything is syncd. You can do this using Promise.all() to generate a promise resolving when all provided promises have resolved.
function setUp() {
Word.run(function(context) {
const promises = [];
for (var i = 0; i < 5; i++) {
var selectedTag = context.document.contentControls.getByTag("myTag");
context.load(selectedTag, 'text');
let p = context.sync().then(function() {
controler.items[0].insertPargraph("Adding paragraph 0")
});
promises.push(p);
}
return Promise.all(promises);
})
}

Related

Node.js Promises within promises not waiting for for loop to return data

The return Promise.all([photoArray]) returns an empty array, seemingly not waiting for the callFB to return its promise that then pushes into the array.
I am not sure what I am doing wrong but am relatively new to Promises with for loops and Ifs.
I am not sure exactly if I am using the correct number of Promises but I seem to not be able to get the 3rd tier Promise.all to wait for the for loop to actually finish (in this scenario, the for loop has to look through many item so this is causing an issue where it is not triggering callFeedback for all the items it should before context.done() gets called.
I have tried using Q.all also for the Promise.all([photoArray]) but have been unable to get that working.
module.exports = function (context, myBlob) {
var res = myBlob
var promiseResolved = checkPhoto(res,context);
var promiseResolved2 = checkVideo(res,context);
Promise.all([promiseResolved, promiseResolved2]).then(function(results){
context.log(results[0], results[1]);
// context.done();
});
});
};
};
function checkPhoto(res, context){
return new Promise((resolve, reject) => {
if (res.photos.length > 0) {
var photoArray = [];
for (var j = 0; j < res.photos.length; j++) {
if (res.photos[j].feedbackId !== null){
var feedbackId = res.photos[j].feedbackId;
var callFB = callFeedback(context, feedbackId);
Promise.all([callFB]).then(function(results){
photoArray.push(results[0]);
});
} else {
photoArray.push("Photo " + j + " has no feedback");
}
}
return Promise.all([photoArray]).then(function(results){
context.log("end results: " + results);
resolve(photoArray);
});
} else {
resolve('No photos');
}
})
}
function checkVideo(res, context){
return new Promise((resolve, reject) => {
same as checkPhoto
})
}
function callFeedback(context, feedbackId) {
return new Promise((resolve, reject) => {
var requestUrl = url.parse( URL );
var requestBody = {
"id": feedbackId
};
// send message to httptrigger to message bot
var body = JSON.stringify( requestBody );
const requestOptions = {
standard
};
var request = https.request(requestOptions, function(res) {
var data ="";
res.on('data', function (chunk) {
data += chunk
// context.log('Data: ' + data)
});
res.on('end', function () {
resolve("callFeedback: " + true);
})
}).on('error', function(error) {
});
request.write(body);
request.end();
})
}
The code suffers from promise construction antipattern. If there's already a promise (Promise.all(...)), there is never a need to create a new one.
Wrong behaviour is caused by that Promise.all(...).then(...) promise isn't chained. Errors aren't handled and photoArray.push(results[0]) causes race conditions because it is evaluated later than Promise.all([photoArray])....
In case things should be processed in parallel:
function checkPhoto(res, context){
if (res.photos.length > 0) {
var photoArray = [];
for (var j = 0; j < res.photos.length; j++) {
if (res.photos[j].feedbackId !== null){
var feedbackId = res.photos[j].feedbackId;
var callFB = callFeedback(context, feedbackId);
// likely no need to wait for callFB result
// and no need for Promise.all
photoArray.push(callFB);
} else {
photoArray.push("Photo " + j + " has no feedback");
}
}
return Promise.all(photoArray); // not [photoArray]
} else {
return 'No photos';
};
}
callFB promises don't depend on each other and thus can safely be resolved concurrently. This allows to process requests faster.
Promise.all serves a good purpose only if it's used to resolve promises in parallel, while the original code tried to resolve the results (results[0]).
In case things should be processed in series the function benefits from async..await:
async function checkPhoto(res, context){
if (res.photos.length > 0) {
var photoArray = [];
for (var j = 0; j < res.photos.length; j++) {
if (res.photos[j].feedbackId !== null){
var feedbackId = res.photos[j].feedbackId;
const callFBResult = await callFeedback(context, feedbackId);
// no need for Promise.all
photoArray.push(callFBResult);
} else {
photoArray.push("Photo " + j + " has no feedback");
}
}
return photoArray; // no need for Promise.all, the array contains results
} else {
return 'No photos';
};
}
Add try..catch to taste.

Value being undefined? [duplicate]

I'm appending onclick events to elements that I'm creating dynamically. I'm using the code below, this is the important part only.
Test.prototype.Show= function (contents) {
for (i = 0; i <= contents.length - 1; i++) {
var menulink = document.createElement('a');
menulink.href = "javascript:;";
menulink.onclick = function () { return that.ClickContent.apply(that, [contents[i]]); };
}
}
First it says that it's undefined. Then I changed and added:
var content = content[i];
menulink.onclick = function () { return that.ClickContent.apply(that, [content]); };
What is happening now is that it always append the last element to all onclick events( aka elements). What I'm doing wrong here?
It's a classical problem. When the callback is called, the loop is finished so the value of i is content.length.
Use this for example :
Test.prototype.Show= function (contents) {
for (var i = 0; i < contents.length; i++) { // no need to have <= and -1
(function(i){ // creates a new variable i
var menulink = document.createElement('a');
menulink.href = "javascript:;";
menulink.onclick = function () { return that.ClickContent.apply(that, [contents[i]]); };
})(i);
}
}
This immediately called function creates a scope for a new variable i, whose value is thus protected.
Better still, separate the code making the handler into a function, both for clarity and to avoid creating and throwing away builder functions unnecessarily:
Test.prototype.Show = function (contents) {
for (var i = 0; i <= contents.length - 1; i++) {
var menulink = document.createElement('a');
menulink.href = "javascript:;";
menulink.onclick = makeHandler(i);
}
function makeHandler(index) {
return function () {
return that.ClickContent.apply(that, [contents[index]]);
};
}
};
A way to avoid this problem altogether, if you don't need compatibility with IE8, is to introduce a scope with forEach, instead of using a for loop:
Test.prototype.Show = function (contents) {
contents.forEach(function(content) {
var menulink = document.createElement('a');
menulink.href = "javascript:;";
menulink.onclick = function() {
return that.ClickContent.call(that, content);
};
});
}

NodeJS - Deferred function inside a loop

I have a conceptual problem with NodeJS... I need to call a deferred function within a loop and use the response for the next iteration. How can I do that without blocking? Here is what I did but of course, response.rev doesn't get updated for the next iteration:
first.function().then(function(response) {
for (var int = 0; int < $scope.files.length; int++) {
call.function(response.rev).then(function (res) {
response.rev = res.rev;
}, function (reason) {
console.log(reason);
});
}
});
Edit, my real code with Benjamin's help (still not working):
pouchWrapper.updateClient(clientManager.clientCleaner($scope.client)).then(function(response) {
if ($scope.files != null) {
var p = $q.when();
for (var int = 0; int < $scope.files.length; int++) {
var doc = $scope.files[int].slice(0, $scope.files[int].size);
p = p.then(function(formerRes){
return pouchWrapper.uploadFiletoDoc($scope.client._id, $scope.contract.policy_number + '&' + $scope.damage.number + '-' + $scope.files[int].name, res.rev, doc, $scope.files[int].type).then(function (res) {
return res;
}, function (reason) {
console.log(reason);
});
});
}
return p;
}
});
You use .then for that. .then is the asynchronous version of the semicolon:
first.function().then(function(response) {
var p = $q.when(); // start with an empty promise
for (var int = 0; int < $scope.files.length; int++) {
p = p.then(function(formerValue){
return call.function(formerValue).then(function (res) {
// use res here as the _current_ value
return res; // return it, so that the next iteration can use it;
});
});
}
return p; // this resolves with the _last_ value
});

Nodejs calling function from within a function

I have 3 methods
exports.getImageById = function (resultFn, id) {
...
}
exports.getCollectionById = function (resultFn, id) {
}
in the third method I want to call both methods
exports.getCollectionImages = function (resultFn, collectionId) {
var arr = new Array();
this.getCollectionById( // fine, 1st call
function (result) {
var images = result.image;
for (i = 0; i < images.length; i++) {
this.getImageById(function (result1) { // error, 2nd call
arr[i] = result1;
}, images[i]
);
}
}
, collectionId
);
resultFn(arr);
}
I can call first function this.getCollectionById but it fails to call this.getImageById, it says undefined function, whats the reason for that?
When you call this.getCollectionById passing it a callback, the callback doesn't have access to the same this
The simplest solution is to save this as a local variable.
exports.getCollectionImages = function (resultFn, collectionId) {
var arr = new Array();
var me = this; // Save this
this.getCollectionById( // fine, 1st call
function (result) {
var images = result.image;
for (var i = 0; i < images.length; i++) {
// Use me instead of this
me.getImageById(function (result1) { // error, 2nd call
arr[i] = result1;
}, images[i]);
}
}, collectionId);
resultFn(arr);
}
The value of this inside the inner function is not the same object as outside, because it's determined depending on how the function is called. You can find a detailed explanation in the MDN article on this.
One of the ways to solve it is by keeping a reference to the outer this in another variable such as that:
var that = this;
this.getCollectionById( // fine, 1st call
function (result) {
var images = result.image;
for (i = 0; i < images.length; i++) {
that.getImageById(function (result1) { // 2nd call
arr[i] = result1;
}, images[i]
);
}
}
, collectionId
);

calling a function having callback in for loop

I want to run window.resolveLocalFileSystemURI(file,success,fail) in for loop passing different file entries and want to return resolved entries in array only after I get all the entries.
function resolveFiles(result,callback)
{
var resultData=[]
window.resolveLocalFileSystemURI(result, function(entry)
{
resolvedGalleryImages.push(entry);
callback(resolvedGalleryImages);
resolvedGalleryImages=[];
}, function(e)
{
alert("err"+e);});
}
//calling--
//#filesarr has captured images uris
for(i = 0; i < filesarr.length; i++)
{
resolveFiles(filesarr[i],function(result){
var resultArr = result;
});
}
How can I prevent callback to be called before I get all the entries.
There are multiple primary ways to attack a problem like this:
Manual coding of an asynchronous loop
Using promises to coordinate multiple async operations
Using a library like Async to coordinate multiple async operations
Here's the manual version:
function getFiles(filesarr, doneCallback) {
var results = new Array(filesarr.length);
var errors = new Array(filesarr.length);
var errorCnt = 0;
var overallCnt = 0;
function checkDone() {
if (overallCnt === filesarr.length) {
if (errorCount) {
doneCallback(errors, results);
} else {
doneCallback(null, results);
}
}
}
for (var i = 0; i < filesarr.length; i++) {
(function(index) {
window.resolveLocalFileSystemURI(url, function (entry) {
results[index] = entry;
++overallCnt;
checkDone();
}, function (e) {
errors[index] = e;
++errorCount;
++overallCnt;
checkDone();
});
})(i);
}
}
getFiles(filesarr, function(errArray, results) {
if (errArray) {
// go errors here
} else {
// process results
}
});
And, here's a version that uses ES6 promises:
// make a promisified function
function resolveFile(url) {
return new Promise(function(resolve, reject) {
window.resolveLocalFileSystemURI(url, resolve, reject);
});
}
function getFiles(filesarr) {
var promises = [];
for (var i = 0; i < filesarr.length; i++) {
promises.push(resolveFile(filesarr[i]));
}
return Promise.all(promises);
}
getFiles(filesarr).then(function(results) {
// process results here
}, function(err) {
// error here
});
this is all based on all your functions being synchronous, and if not: be more specific what you're using. (there is no jQuery here yet your tags say jquery)
function resolveFile(path) {
var result;
window.resolveLocalFileSystemURI(path, function(file) {
result = file;
});
return file;
}
var resolvedFiles = filesarr.map(resolveFile);
callback(resolvedFiles);

Categories