Here I'm calling the function testlike() in each loop , the testlike function is called but before completion of that function the loop is completed ,after completion of loop inside the DataServices.GetLikes(value).then(function (resp1) ) function is executing , i want to continue the loop after completion of testlike() function in everytime calling
function getAllMessages()
{
DataServices.getAllMessages().then(function (resp) {
if (resp.d.results.length > 0) {
$.each(resp.d.results, function (key, val) {
testlike(val.Id);
});
}
});
}
function testlike(value)
{
DataServices.GetLikes(value).then(function (resp1) {
if (resp1.d.results.length > 0) {
$.each(resp1.d.results, function (k, v) {
if (value === v.MessageIdId) {
$(".eventfire").removeClass("likeevent").addClass("unlikeevent");
$(".unlikeevent").text("Unlike");
$(".unlikeevent").attr("unlikeitem", v.Id);
}
});
$(".unlikeevent").click(function () {
unLikeevent($(this).attr("unlikeitem"));
});
}
}, function (err) { alert(err) });
}
Your, testlike function is async, but your $.each loop is sync.
Not sure i understand you correctly but will try my best
first of all you don't need to check if the array item as any length. just loop over the array and if it's empty it won't do anything.
Second, you don't need to use jQuery's $.each loop. Arrays has methods for that: forEach and map
This will do all testlike in parallel and log all done when everything is complete
function getAllMessages() {
DataServices.getAllMessages().then(function(resp) {
// create a new array from results that are only promises
var jobs = resp.d.results.map(function(val) {
return testlike(val.Id)
})
// Wait for all to complete
Promise.all(jobs).then(function(results){
// It's done
console.log('all done')
})
})
}
function testlike(value) {
// here i return the value/promise
return DataServices.GetLikes(value).then(function (resp1) {
resp1.d.results.forEach(function (v, k) {
if (value === v.MessageIdId) {
$(".eventfire").removeClass("likeevent").addClass("unlikeevent")
$(".unlikeevent").text("Unlike").attr("unlikeitem", v.Id)
}
})
$(".unlikeevent").click(function () {
unLikeevent($(this).attr("unlikeitem"))
})
}, function (err) {
alert(err)
})
}
but if you want to do it one after the other, then i would change the getAllMessage to use async/await
async function getAllMessages() {
var resp = DataServices.getAllMessages()
for (let job of resp.d.results) {
await testlike(val.Id)
}
}
but there is alternative methods as well if you need a es5 version
just comment here if you need one and i will make something up
Related
I am writing a recursive function inside for loop like below:
var output = [];
function myFunc(myValue, callback) {
myAnotherFunc(myValue, function(result){
for (var i=0; i < result.myKey.length; i++){
if(result.myKey[i].name === 'something'){
myFunc(result.myKey[i].recurseValue, function(recursiveResult){
//some recursive stuff
output.push(recursiveResult.someValue)
});
}
}
});
}
And initiating the recursive function like below:
myFunc(initialValue, function(result){
//some stuff
});
Its working fine, but how do I know when my recursive flow ends so that I can do something else from the final output?
You can use Promises™! It's basically a way to defer a callback till after an Asynchronous flow is completed: Example:
// Instead of passing your normal callback, we'll tell the
// function to use resolve(results) to pass your results to the
// next code block so it can do something after all your recursions are completed
const someTask = new Promise(resolve => myFunc(initialValue, resolve))
someTask.then(result => {
/* Do Something with the results at the end of aformentioned callback hell :D */
})
PS. You also have to modify your original function signature to:
function myFunc(myValue, callback) {
myAnotherFunc(myValue, function(result){
const cbks = [] //Store the async resuls of all myFunc() executions
for (i=0; i < result.myKey.length; i++){
if(results[i] === 'something'){
cbks.push(new Promise(res => myFunc(result[i].recurseValue, res)))
}
}
//Run all async myFunc() and return the results in an array
Promise.all(cbks).then(callback)
});
}
function myFunc(resolve) {
var rec = function(myVal, cb) {
myOther(recurseValue, function(result) {
var hasName = result[myKey].filter(function(obj) {
return obj.name === 'something';
})[0];
if (hasName) {
rec(hasName[recurseValue], function(recResult) {
// other recursive stuff
});
} else {
resolve(?); // whatever the final value should be
}
});
};
return rec;
}
function recurseAsync(f, initial) {
return new Promise(function(resolve, reject) {
f(resolve)(initial);
});
}
Couple notes.
The recurseAsync function takes a function that takes a resolution callback and returns a recursive function that calls that callback when finished to resolve the promise. myFunc has been altered to fit that format.
I used array filtering rather than a for loop and shortened some names. Also if you are using a variable for object access use [] instead of .. To use the final value when all of this is finished you can call .then on the promise.
// whatever initial value 'foo' should be
var finished = recurseAsync(myFunc, foo);
finished.then(function(finalValue) {
// do something with the final result of all the recursion
});
I have a variable oldBindings which record all the existing bindings of an Excel table. I have built BindingDataChanged listeners based on oldBindings. So when newBindings come up, I need to remove all the old listeners linked to oldBindings and add new listeners based on newBindings. At the moment, I have written the following code:
var oldBindings = ["myBind1", "myBind2"]; // can be updated by other functions
function updateEventHandlers(newBindings) {
removeEventHandlers(oldBindings, function () {
addEventHandlers(newBindings)
})
}
function removeEventHandlers(oldBindings, cb) {
for (var i = 0; i < oldBindings.length; i++) {
Office.select("binding#"+oldBindings[i]).removeHandlerAsync(Office.EventType.BindingDataChanged, function (asyncResult) {
Office.context.document.bindings.releaseByIdAsync(oldBindings[i], function () {});
});
}
cb()
}
As removeHandlerAsync and releaseByIdAsync are built with callback rather than promise, I need to organise the whole code with callback. There are 2 things I am not sure:
1) in removeEventHandlers, will cb() ALWAYS be executed after the removal of all the listeners? How could I ensure that?
2) Do I have to make addEventHandlers as a callback of removeEventHandlers to ensure their execution order?
1) in removeEventHandlers, will cb() ALWAYS be executed after the removal of all the listeners?
No. It'll be called after the initiation of the removal. But if the removal is async, it may be called before the removal is complete.
2) Do I have to make addEventHandlers as a callback of removeEventHandlers to ensure their execution order?
Yes, but not the way you have. The way you have is just like doing
removeEventHandlers();
addEventHandlers();
because you call cb at the end of removeEventHandlers without waiting for anything to finish.
As removeHandlerAsync and releaseByIdAsync are built with callback rather than promise, I need to organise the whole code with callback.
Or you could give yourself Promise versions of them. More on that in a moment.
Using the non-Promise callback approach, to ensure you call cb from removeEventHandlers when all the work is done, remember how many callbacks you're expecting and wait until you get that many before calling cb:
var oldBindings = ["myBind1", "myBind2"]; // can be updated by other functions
function updateEventHandlers(newBindings) {
removeEventHandlers(oldBindings, function() {
addEventHandlers(newBindings);
});
}
function removeEventHandlers(oldBindings, cb) {
var waitingFor = oldBindings.length;
for (var i = 0; i < oldBindings.length; i++) {
Office.select("binding#"+oldBindings[i]).removeHandlerAsync(Office.EventType.BindingDataChanged, function (asyncResult) {
Office.context.document.bindings.releaseByIdAsync(oldBindings[i], function () {
if (--waitingFor == 0) {
cb();
}
});
});
}
}
But any time you have a callback system, you can Promise-ify it:
function removeHandlerPromise(obj, eventType) {
return new Promise(function(resolve, reject) {
obj.removeHandlerAsync(eventType, function(asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
reject(asyncResult.error);
} else {
resolve(asyncResult.value);
}
});
});
}
function releaseByIdPromise(obj, value) {
return new Promise(function(resolve, reject) {
obj.releaseByIdAsync(value, function(asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
reject(asyncResult.error);
} else {
resolve(asyncResult.value);
}
});
});
}
Then that lets you do this:
var oldBindings = ["myBind1", "myBind2"]; // can be updated by other functions
function updateEventHandlers(newBindings) {
removeEventHandlers(oldBindings).then(function() {
addEventHandlers(newBindings);
});
}
function removeEventHandlers(oldBindings) {
return Promise.all(oldBindings.map(function(binding) {
return removeHandlerPromise(Office.select("binding#"+binding), Office.EventType.BindingDataChanged).then(function() {
return releaseByIdPromise(Office.context.document.bindings, binding);
});
});
}
Or you can give yourself a generic Promise-ifier for any async op that returns an AsyncResult:
function promisify(obj, method) {
var args = Array.prototype.slice.call(arguments, 2);
return new Promise(function(resolve, reject) {
args.push(function(asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
reject(asyncResult.error);
} else {
resolve(asyncResult.value);
}
});
obj[method].apply(obj, args);
});
}
Then that lets you do this:
var oldBindings = ["myBind1", "myBind2"]; // can be updated by other functions
function updateEventHandlers(newBindings) {
removeEventHandlers(oldBindings).then(function() {
addEventHandlers(newBindings);
});
}
function removeEventHandlers(oldBindings) {
return Promise.all(oldBindings.map(function(binding) {
return promisify(Office.select("binding#"+binding), "removeHandlerAsync", Office.EventType.BindingDataChanged).then(function() {
return promisify(Office.context.document.bindings, "releaseByIdAsync", binding);
});
});
}
I'm trying to create a bounce effect on an image after an synchronous AND an asynchronous call but can't figure out how to do it. The problem I'm having now is that I sometimes get both the bounce effect and afterwards isExecuted becomes true because the asynchronous event takes some time.
My code should work like this:
Iterate over each object in myEnum and execute the following
if myCondition1 is equal too myCondition2 set isExecuted = true
if above is not true, call an asynchronous method which evaluates some stuff, if it's true it will set isExecuted = true.
wait for all above is finished, then if isExecuted still is false, bounce the image.
Here's the code:
var isExecuted = false;
myEnum.each()
{
if (myCondition1 == myCondition2) { //Synchronous
isExecuted = true;
return false; //stop the iteration
}
else {
MyAsyncFunction(myCondition2, function(isTrue) { // Asynchronous
if (isTrue) {
isExecuted = true;
return false; //stop the iteration
}
});
}
});
// Execute this ONLY when above code is finished executing
if (isExecuted == false) {
BounceImage();
}
Please note that the async function is not always executed but the bounce check must always be executed if isExecuted is true.
This whole setup won't work as you want because you cannot stop the iteration from the asynchronous callback. Instead you have to process the array (or whatever myEnum is) asynchronously and do something afterwards. I highly recommend to learn about promises.
function process(array, cb) {
var i = 0;
function p(v) {
return new Promise((resolve, reject) => {
try {
// call the callback, passing in the current value and
// a callback to control execution
cb(v, function next(stop, result) {
if (stop) {
// if stop = true, abort the iteration and resolve the passed in value
resolve(result);
} else if (i < array.length) {
// else if there are more elements left, process them
resolve(p(array[i++]));
} else { // else resolve to the passed in value
resolve(result);
}
});
} catch(e) {
reject(e);
}
});
}
// start with the first element
return p(array[0]);
}
process([1,2,3], function(v, next) {
if (v == 2) {
return next(true, v);
}
next();
}).then(result => console.log(result));
Applied to your code it would look something like
process(myEnum, function(v, next) {
if (myCondition1 == myCondition2) {
return next(true, true);
} else {
MyAsyncFunction(myCondition2, function(isTrue) {
if (isTrue) {
return next(true, true);
}
next();
});
}
}).then(function(isExecuted) {
if (!isExecuted) {
BounceImage();
}
});
Of course you can also use an existing library that allows you to do this. There a many different (potentially more elegant) ways to achieve this.
Instead, use callbacks:
function asyncFunction (a, b, c, callback) {
...
callback();
}
asyncFunction(1, 2, 3, function () {
doSomethingOnceDone();
});
This is a very common practice. It's how most async Chrome APIS do it, just to name one thing.
How to correctly construct a loop to make sure the following promise call and the chained logger.log(res) runs synchronously through iteration? (bluebird)
db.getUser(email).then(function(res) { logger.log(res); }); // this is a promise
I tried the following way (method from http://blog.victorquinn.com/javascript-promise-while-loop )
var Promise = require('bluebird');
var promiseWhile = function(condition, action) {
var resolver = Promise.defer();
var loop = function() {
if (!condition()) return resolver.resolve();
return Promise.cast(action())
.then(loop)
.catch(resolver.reject);
};
process.nextTick(loop);
return resolver.promise;
});
var count = 0;
promiseWhile(function() {
return count < 10;
}, function() {
return new Promise(function(resolve, reject) {
db.getUser(email)
.then(function(res) {
logger.log(res);
count++;
resolve();
});
});
}).then(function() {
console.log('all done');
});
Although it seems to work, but I don't think it guarantees the order of calling logger.log(res);
Any suggestions?
If you really want a general promiseWhen() function for this and other purposes, then by all means do so, using Bergi's simplifications. However, because of the way promises work, passing callbacks in this way is generally unnecessary and forces you to jump through complex little hoops.
As far as I can tell you're trying :
to asynchronously fetch a series of user details for a collection of email addresses (at least, that's the only scenario that makes sense).
to do so by building a .then() chain via recursion.
to maintain the original order when handling the returned results.
Defined thus, the problem is actually the one discussed under "The Collection Kerfuffle" in Promise Anti-patterns, which offers two simple solutions :
parallel asynchronous calls using Array.prototype.map()
serial asynchronous calls using Array.prototype.reduce().
The parallel approach will (straightforwardly) give the issue that you are trying to avoid - that the order of the responses is uncertain. The serial approach will build the required .then() chain - flat - no recursion.
function fetchUserDetails(arr) {
return arr.reduce(function(promise, email) {
return promise.then(function() {
return db.getUser(email).done(function(res) {
logger.log(res);
});
});
}, Promise.resolve());
}
Call as follows :
//Compose here, by whatever means, an array of email addresses.
var arrayOfEmailAddys = [...];
fetchUserDetails(arrayOfEmailAddys).then(function() {
console.log('all done');
});
As you can see, there's no need for the ugly outer var count or it's associated condition function. The limit (of 10 in the question) is determined entirely by the length of the array arrayOfEmailAddys.
I don't think it guarantees the order of calling logger.log(res);
Actually, it does. That statement is executed before the resolve call.
Any suggestions?
Lots. The most important is your use of the create-promise-manually antipattern - just do only
promiseWhile(…, function() {
return db.getUser(email)
.then(function(res) {
logger.log(res);
count++;
});
})…
Second, that while function could be simplified a lot:
var promiseWhile = Promise.method(function(condition, action) {
if (!condition()) return;
return action().then(promiseWhile.bind(null, condition, action));
});
Third, I would not use a while loop (with a closure variable) but a for loop:
var promiseFor = Promise.method(function(condition, action, value) {
if (!condition(value)) return value;
return action(value).then(promiseFor.bind(null, condition, action));
});
promiseFor(function(count) {
return count < 10;
}, function(count) {
return db.getUser(email)
.then(function(res) {
logger.log(res);
return ++count;
});
}, 0).then(console.log.bind(console, 'all done'));
Here's how I do it with the standard Promise object.
// Given async function sayHi
function sayHi() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Hi');
resolve();
}, 3000);
});
}
// And an array of async functions to loop through
const asyncArray = [sayHi, sayHi, sayHi];
// We create the start of a promise chain
let chain = Promise.resolve();
// And append each function in the array to the promise chain
for (const func of asyncArray) {
chain = chain.then(func);
}
// Output:
// Hi
// Hi (After 3 seconds)
// Hi (After 3 more seconds)
Given
asyncFn function
array of items
Required
promise chaining .then()'s in series (in order)
native es6
Solution
let asyncFn = (item) => {
return new Promise((resolve, reject) => {
setTimeout( () => {console.log(item); resolve(true)}, 1000 )
})
}
// asyncFn('a')
// .then(()=>{return async('b')})
// .then(()=>{return async('c')})
// .then(()=>{return async('d')})
let a = ['a','b','c','d']
a.reduce((previous, current, index, array) => {
return previous // initiates the promise chain
.then(()=>{return asyncFn(array[index])}) //adds .then() promise for each item
}, Promise.resolve())
There is a new way to solve this and it's by using async/await.
async function myFunction() {
while(/* my condition */) {
const res = await db.getUser(email);
logger.log(res);
}
}
myFunction().then(() => {
/* do other stuff */
})
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
https://ponyfoo.com/articles/understanding-javascript-async-await
Bergi's suggested function is really nice:
var promiseWhile = Promise.method(function(condition, action) {
if (!condition()) return;
return action().then(promiseWhile.bind(null, condition, action));
});
Still I want to make a tiny addition, which makes sense, when using promises:
var promiseWhile = Promise.method(function(condition, action, lastValue) {
if (!condition()) return lastValue;
return action().then(promiseWhile.bind(null, condition, action));
});
This way the while loop can be embedded into a promise chain and resolves with lastValue (also if the action() is never run). See example:
var count = 10;
util.promiseWhile(
function condition() {
return count > 0;
},
function action() {
return new Promise(function(resolve, reject) {
count = count - 1;
resolve(count)
})
},
count)
I'd make something like this:
var request = []
while(count<10){
request.push(db.getUser(email).then(function(res) { return res; }));
count++
};
Promise.all(request).then((dataAll)=>{
for (var i = 0; i < dataAll.length; i++) {
logger.log(dataAll[i]);
}
});
in this way, dataAll is an ordered array of all element to log. And log operation will perform when all promises are done.
First take array of promises(promise array) and after resolve these promise array using Promise.all(promisearray).
var arry=['raju','ram','abdul','kruthika'];
var promiseArry=[];
for(var i=0;i<arry.length;i++) {
promiseArry.push(dbFechFun(arry[i]));
}
Promise.all(promiseArry)
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error);
});
function dbFetchFun(name) {
// we need to return a promise
return db.find({name:name}); // any db operation we can write hear
}
Use async and await (es6):
function taskAsync(paramets){
return new Promise((reslove,reject)=>{
//your logic after reslove(respoce) or reject(error)
})
}
async function fName(){
let arry=['list of items'];
for(var i=0;i<arry.length;i++){
let result=await(taskAsync('parameters'));
}
}
function promiseLoop(promiseFunc, paramsGetter, conditionChecker, eachFunc, delay) {
function callNext() {
return promiseFunc.apply(null, paramsGetter())
.then(eachFunc)
}
function loop(promise, fn) {
if (delay) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, delay);
})
.then(function() {
return promise
.then(fn)
.then(function(condition) {
if (!condition) {
return true;
}
return loop(callNext(), fn)
})
});
}
return promise
.then(fn)
.then(function(condition) {
if (!condition) {
return true;
}
return loop(callNext(), fn)
})
}
return loop(callNext(), conditionChecker);
}
function makeRequest(param) {
return new Promise(function(resolve, reject) {
var req = https.request(function(res) {
var data = '';
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
resolve(data);
});
});
req.on('error', function(e) {
reject(e);
});
req.write(param);
req.end();
})
}
function getSomething() {
var param = 0;
var limit = 10;
var results = [];
function paramGetter() {
return [param];
}
function conditionChecker() {
return param <= limit;
}
function callback(result) {
results.push(result);
param++;
}
return promiseLoop(makeRequest, paramGetter, conditionChecker, callback)
.then(function() {
return results;
});
}
getSomething().then(function(res) {
console.log('results', res);
}).catch(function(err) {
console.log('some error along the way', err);
});
How about this one using BlueBird?
function fetchUserDetails(arr) {
return Promise.each(arr, function(email) {
return db.getUser(email).done(function(res) {
logger.log(res);
});
});
}
Here's another method (ES6 w/std Promise). Uses lodash/underscore type exit criteria (return === false). Note that you could easily add an exitIf() method in options to run in doOne().
const whilePromise = (fnReturningPromise,options = {}) => {
// loop until fnReturningPromise() === false
// options.delay - setTimeout ms (set to 0 for 1 tick to make non-blocking)
return new Promise((resolve,reject) => {
const doOne = () => {
fnReturningPromise()
.then((...args) => {
if (args.length && args[0] === false) {
resolve(...args);
} else {
iterate();
}
})
};
const iterate = () => {
if (options.delay !== undefined) {
setTimeout(doOne,options.delay);
} else {
doOne();
}
}
Promise.resolve()
.then(iterate)
.catch(reject)
})
};
Using the standard promise object, and having the promise return the results.
function promiseMap (data, f) {
const reducer = (promise, x) =>
promise.then(acc => f(x).then(y => acc.push(y) && acc))
return data.reduce(reducer, Promise.resolve([]))
}
var emails = []
function getUser(email) {
return db.getUser(email)
}
promiseMap(emails, getUser).then(emails => {
console.log(emails)
})
I am getting used to $.Deferred just now, and it happens that I needed to chain three tasks using $.Deferred - then.
I created a function:
function processA(param1, param2) {
log = $.post('http://domain.com/process',
{
id: param1,
another: param2
}
),
set = log.then(function(html){
if (someCondition) {
console.log('Successful');
// i want to do another ajax call here
// and chain another, that does an ajax again
}
})
}
How do I do that, as stated in the comments of my code.
Is it right? Not tested, just thought while typing this.
set = log.then(function(html){
if (someCondition) {
console.log('Successful');
$.post(....),
def = set.then(function(data){
// i want to do another thing here.
})
}
})
And another thing, is it possible to use the function in a loop?
e.g.
data = [{param1:"...", param2:"..."}, {..., ...}]
$.each(data, function(k,v){
processA(v.param1, v.param2);
})
Here better explained chaining promises:
function authenticate() {
return getUsername()
.then(function (username) {
return getUser(username);
})
// chained because we will not need the user name in the next event
.then(function (user) {
return getPassword()
// nested because we need both user and password next
.then(function (password) {
if (user.passwordHash !== hash(password)) {
throw new Error("Can't authenticate");
}
});
});
}
This is something I wrote for a dice game. Rolling the dice, each dice.roll() gets stored into a $.Deferred object. When all dice have run their animation, you can execute your callbacks or whatever.
var promises = [];
// collect promises
_.each(fDice, function (die) {
promises.push(function () {
return $.Deferred(function (dfd) {
$(die).roll(options, function (data) {
dfd.resolve(data); // resolve the state
});
}).promise(); // send it back
});
});
// roll selected dice
dfrAll(promises).done(function () {
// do something here
});
dfrAll: function (array) {
/// <summary>Resolves n $.Deferred functions from an Array</summary>
/// <param name="Array" type="Array">An Array of $.Deferred functions</param>
/// <returns type="Deferred" />
var dfd = $.Deferred(),
len = array.length,
results = [];
if (len === 0) {
dfd.resolve(results);
} else {
for (var i = 0; i < len; i++) {
var promise = array[i];
$.when(promise()).then(function (value) {
results.push(value);
if (results.length === len) {
dfd.resolve(results);
}
});
}
}
return dfd.promise();
}
For me the storage of deferred functions into an array was the key to resolve my animations. Hope it helps.