Few minutes ago I asked about looping, see Asynchronous for cycle in JavaScript.
This time, my question is - Is there any module for Node.js?
for ( /* ... */ ) {
// Wait! I need to finish this async function
someFunction(param1, praram2, function(result) {
// Okay, continue
})
}
alert("For cycle ended");
Is is that hard to move the stuff over into a module?
EDIT: Updated the code.
function asyncLoop(iterations, func, callback) {
var index = 0;
var done = false;
var loop = {
next: function() {
if (done) {
return;
}
if (index < iterations) {
index++;
func(loop);
} else {
done = true;
callback();
}
},
iteration: function() {
return index - 1;
},
break: function() {
done = true;
callback();
}
};
loop.next();
return loop;
}
exports.asyncFor = asyncLoop;
And a small test:
// test.js
var asyncFor = require('./aloop').asyncFor; // './' import the module relative
asyncFor(10, function(loop) {
console.log(loop.iteration());
if (loop.iteration() === 5) {
return loop.break();
}
loop.next();
},
function(){console.log('done')}
);
Rest is up to you, it's impossible to make this 100% generic.
Yes, you can use Async.js.
Async is a utility module which
provides straight-forward, powerful
functions for working with
asynchronous JavaScript. Although
originally designed for use with
node.js, it can also be used directly
in the browser.
Related
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 have this function:
waitForFreeAccnt.prototype.isMemberFree = function () {
var self = this;
self.api.getMemberInfo(function () {
var accType = self.api.connect.accountType;
console.log(accType);
if (accType === 'FREE') {
console.log('it is free');
return true;
} else {
console.log('it is not free');
return false;
}
});
};
I would like to wait till the account is free for up to 10 seconds with something like that:
var test = function () {
for (var start = 1; start < 10; start++) {
var result = self.isMemberFree();
console.log(result);
if (result) {
break;
} else {
self.api.pause(1000);
console.log('waiting');
}
}
};
But it doesn't work because self.api.getMemberInfo is asynch call. This is super frustrating with Javascript. Any other language it would be so simple to do. How do I force the for loop to wait for self.isMemberFree() to finish executing before proceeding with the loop?
Also to note, this is not in browser execution so I don't care about anything hanging.
When dealing with asynchronous code, you need to make use of callbacks. That is, if you want to do a() and b() in order but a() does something asynchronously, then you need to call b() from within a() once a() has a result. So not:
a(); // does something asynchronously
b(); // tries to use a()'s result but it isn't available yet
... but rather
a(b); // pass b to a() and a() will call it when ready
function a(callback) {
triggerAsyncFunction(function(result) {
if (result === something)
callback("a just finished");
});
}
Note that a() doesn't refer to b() by name, it just calls whatever function is passed in as an argument.
So applying that to your code, maybe something like this:
waitForFreeAccnt.prototype.isMemberFree = function (cbf) {
var self = this;
self.api.getMemberInfo(function () {
cbf(self.api.connect.accountType === 'FREE');
});
};
waitForFreeAccnt.prototype.testMemberXTimes = function(maxAttempts, callback) {
var attempts = 0;
var self = this;
(function attempt() {
self.isMemberFree(function(free) {
if (free)
callback(true);
else if (++attempts < maxAttempts)
setTimeout(attempt, 1000);
else
callback(false);
});
)();
};
this.testMemberXTimes(10, function(isFree) {
// the next part of your code here, or called from here
// because at this point we know we've tested up to
// ten times and isFree tells us the result
});
Note that the way I coded getMemberInfo() it is basically doing the same thing yours was, but instead of returning a boolean it is calling the callback function and passing the same boolean value that you were returning. (I've removed the console.log()s to make the code shorter.)
Note also that you could structure the above to use promises, but the end result will be the same.
You could return a Promise
waitForFreeAccnt.prototype.isMemberFree = function () {
return new Promise((reject, resolve)=>
// set a timeout if api call takes too long
var timeout = setTimeout(()=> reject(Error('API timeout')), 10000);
// make api call
this.api.getMemberInfo(()=> {
clearTimeout(timeout);
resolve(this.api.connect.accountType === 'FREE');
});
);
};
Then use it like this
whatever.isMemberFree().then(isFree=> {
if (isFree)
console.log('it is free');
else
console.log('it is not free');
})
// handle timeout or other errors
.catch(err=> {
console.log(err.message);
});
Building on naomik's answer, if you do it that way you can pretty easily use a for loop with it, using the (most likely) upcoming async/await feature - though it's not part of ES2015.
// Note "async" here! That will make "await" work. It makes the function
// return a promise, which you'll be able to either "await" or
// "test().then" later.
var test = async function () {
for (var start = 1; start < 10; start++) {
// Right here we're using "await" - it makes JavaScript *wait* for
// the promise that comes from self.isMemberFree() to be finished.
// It's really handy because you can use it in loops like "for" and
// "while" without changing the flow of your program!
var result = await self.isMemberFree();
console.log(result);
if (result) {
break;
} else {
self.api.pause(1000);
console.log('waiting');
}
}
};
For now you'll need to use a transpiler like Babel or Traceur before you can really use async/await, though. It's only supported in Microsoft Edge 14 right now.
And a big emphasis that what is returned from test() isn't whatever you directly return from inside it. If I do this:
var test = async function() { return 15; };
var result = test();
I'm not going to get 15 - I'll get a promise that will resolve as 15:
result.then(function(res) {
console.log(res); // 15
});
// or, use an async function again:
var main = async function() {
console.log(await res); // 15
};
main();
I don't have my work laptop today because it is Sunday, I'm coding this on sublime. Apologise if the syntax is a bit off.
To solve your problem I would recommend changing isMemberFree() to take in a callback function. This is because isMemberFree is async, and you will need a way to report the result after it has done the work.
Then change test function to use setTimeout API to wait a second.
Wrap the function call for isMemberFree() to be in a nested function and call it recursively, that way you'll have synchronize control over the async calls.
Look at the coding example:
waitForFreeAccnt.prototype.isMemberFree = function (done) {
var self = this;
self.api.getMemberInfo(function () {
var accType = self.api.connect.accountType;
console.log(accType);
if (accType === 'FREE') {
console.log('it is free');
return done(null, true);
} else {
console.log('it is not free');
return done(null, false);
}
});
};
var test = function () {
var testMembership = function(waitAttempt, isFree) {
if (isFree) {
return;
}
else if (waitAttempt > 10) {
// wait exceeded, do something.
return;
}
setTimeout(function() {
self.isMemberFree(function(err, isFree) {
testMembership(waitAttempt+=1, isFree);
});
}, /*total milliseconds in 1sec=*/1000);
}
testMembership(/*WaitAttempts=*/0, /*isFree=*/false);
};
What the above code does is that, presumably something has already been done to the member's account and now test function is called. So it waits for 1 second, then call isMemberFree function, this happens recursively until either isMemberFree() returns true OR the 10 seconds wait has been exceeded.
So, I have a delayed function called delayed1 that gets repeated everytime cond1 is true, the problem is, somewhere inside that delayed1 function do_something1() gets called and if cond3 is true, another delayed function called delayed2 gets called.
Now the problem is I want delayed1 to wait until delayed2 is finished (that means until cond2 is false) and then resume again, which doesn't seem to be happening. Once delayed2 gets started, delayed1 doesn't wait for it to finish and then resume. Is there a way to do this? Hopefully I was clear enough...
function delayed2() {
setTimeout(function () {
do_something2();
if ( cond2 ) {
delayed2();
}
}, 1000)
}
function do_something1(){
if ( cond3 ){
delayed2();
}
else {
something_else();
}
}
function delayed1() {
does_something_here();
setTimeout(function () {
do_something1();
if ( cond1 ){
delayed1();
}
}, 1000)
}
I see what your are trying to do here. You may probably want to approach in a different way. The best way would be to use promises or Deferreds.
Here is the example using jquery deferreds of your given code.
var wait_a_second = $.Deffered();
$.when(wait_a_second)
.then(does_something_here)
.then(function(){
if( cond1 ) delayed1();
})
.then(delayed1);
function delayed1(){
var d = $.Deffered()
// Your conditions and logic
if( cond ) {
d.resolve();
}
return d.promise();
}
function delayed2(){
var d = $.Deffered();
// Your conditions and logic
if( cond ) {
d.resolve();
}
return d.promise();
}
In short learn promises to manage async operations queue.
You should use promises to implement synchronous execution in JS.
Promise is a mechanism which returns an object to which you can hook callbacks. These callbacks can be called once a specific function is done with execution. Here is how it applies to your situation.
You generally have 3 functions which depend on each other. The first one should wait for the second and the second one should wait for the third. In the first level the second function should return a Promise.
function delayed1() {
does_something_here();
setTimeout(function () {
var promise = do_something1();
promise.then(function(){
if ( cond1 ){
delayed1();
}
});
}, 1000)
}
Here is your second function. It creates a promise and returns it. Also inside its if ( cond3 ) it should wait for a promise from the third function. I'm not going to write your third function as I'm certain that you can do it yourself following this pattern.
function do_something1(){
return new Promise(function(resolve){
if ( cond3 ){
var promise = delayed(2);
promise.then(function(){
resolve()
})
}
else {
something_else();
resolve();
}
})
}
You can try using callbacks. Following is a basic representation:
var count = 0;
function delay1(callback) {
setTimeout(function() {
console.log("Delay 1");
count++;
if (count < 12) {
if (count % 2 == 0) {
delay1();
} else {
delay2()
}
}
if (callback) {
callback();
}
}, 1000);
}
function delay2(callback) {
setTimeout(function() {
console.log("Delay 2");
if (count > 10) {
delay1(function() {
console.log("This is callback;")
})
} else if (count % 2 == 0) {
delay2();
} else {
delay1()
}
if (callback) {
callback();
}
count++;
}, 1000);
}
delay1();
I have an async function like:
$scope.get_device_version = function(){
return $q(function(resolve, reject){
$http.get('/api/get_version')
.then(function(response) {
resolve(response.data.version);
},function(response) {
reject("Lost connection..");
});
});
};
Question 1: Now I want to run that function 10 times in a row sequentially, what do I do?
Question 2: Now I want to run the function until I get a desired answer from the http-request, what do I do?
Question 1
.then(callback) returns a new promise resolving to the return value of callback, or if the return value is another promise, to the settled value of that promise. This can be used to chain asynchronous calls. For example, given
function myFunction() { return promise; }
the following snippet will call myFunction 10 times sequentially
var result = $q.resolve();
for (var i = 0; i < 10; ++i) {
result = result.then(myFunction);
}
result.then(function () { /* myFunction has been called 10 times */ })
Question 2
Here, recursion is required since you don't know the exact number of times you will need to iterate. For example, given myFunction as above,
function wrapper() {
var deferred = $q.defer();
iter();
function iter(res) {
// skip first iter() call, wait for acceptable res
if (result !== void 0 && /* res is what I want */) {
return deferred.resolve(res);
}
// recurse
return myFunction().then(iter);
}
return deferred.promise;
}
wrapper().then(function (res) { /* res is an acceptable response */ })
Note that for this use case, promises do not really offer an advantage over simple callbacks.
Question 1:
var request = null;
for( var i = 0; i < 10; i++ ) {
if( request )
request.then( $scope.get_device_version );
else
request = $scope.get_device_version();
}
Question 2:
$scope.get_device_version = function(){
return $q(function(resolve, reject){
$http.get('/api/get_version')
.then(function(response) {
if( /*Not a desired answer? run again:*/ )
$scope.get_device_version()
else
resolve(response.data.version);
},function(response) {
reject("Lost connection..");
});
});
};
This is pseudo code, i have not tested it.
var checkDeviceVersion=function (times) {
$scope.get_device_version().then(function(data) {
if(times==0) return;
checkDeviceVersion(times-1);
});
}
var keepChecking=function () {
$scope.get_device_version().then(function(data) {
if(data.condition) return;
keepChecking();
});
}
Invoke then as checkDeviceVersion(10) or keepChecking()
I want to get two resources using two asynch calls. I want to proceed only when both resources have been retrieved.
How can I do this elegantly in JS?
This would work:
getStuff1(function (result1) {
getStuff2 (function (result2) {
// do stuff with result1 and result2
....
}
}
but stuff2 only starts after stuff1 completes. I'd prefer to start stuff2 while waiting on stuff1.
If you know that functions are in fact first-class objects in Javascript, you can come up with a fairly elegant solution.
Without any extra objects, or global variables.
function callback1() {
callback1.done = true;
commonCallback();
}
function callback2() {
callback2.done = true;
commonCallback();
}
function commonCallback() {
if (callback1.done && callback2.done) {
// do stuff, since you know both calls have come back.
}
}
Why is this so elegant? Because you've encapsulated the data, your scope is free from useless variables and the code is more readable than ever. How cool is that? :)
UPDATE
And if you want a bit more general solution you may try the following:
function callback() {
callback.number -= 1;
if (callback.number === 0) {
// do stuff since all calls finished
callback.last();
}
}
callback.newQueue = function(num, last) {
callback.number = num;
callback.last = last;
}
// EXAMPLE USAGE
// our last function to be invoked
function afterEverythingDone(){ alert("done"); }
// create a new callback queue
callback.newQueue(3, afterEverythingDone);
// as time passes you call the callback
// function after every request
callback();
callback();
callback();
// after all call is finished
// afterEverythingDone() executes
Awesomeness again :)
One way is to use the same callback for both requests and proceed when both are complete:
var requestStatus = {
fooComplete: false,
barComplete: false
};
function callback(data) {
if (isFoo(data)) {
requestStatus.fooComplete = true;
} else if (isBar(data)) {
requestStatus.barComplete = true;
}
if (requestStatus.fooComplete && requestStatus.barComplete) {
proceed();
}
}
getAsync("foo", callback);
getAsync("bar", callback);
You'll probably want to flesh this out into a class.
Edit: added the async calls for clarity
You could have the callback function for each one indicate that their respective request has come back, and then execute the same common function. To illustrate:
var call1isBack = false;
var call2isBack = false;
function call1Callback() {
call1isBack = true;
commonCallback();
}
function call2Callback() {
call2isBack = true;
commonCallback();
}
function commonCallback() {
if (call1isBack && call2isBack) {
// do stuff, since you know both calls have come back.
}
}
Use a common callback handler with a counter that only allows passage into the "actual" processing section after the counter meets or exceeds the number of pending requests:
var commonHandler = (function() {
var counter=0, pendingCalls=2;
return function() {
if (++counter >= pendingCalls) {
// Do the actual thing...
}
}
})();
makeAjaxCall({args:args1, onComplete:commonHandler});
makeAjaxCall({args:args2, onComplete:commonHandler});
Using a closure around the anonymous function lets you avoid using a global variable for the counter.
Here's a snippet from a concurrent library I'm working on. All you need to do is instantiate a new Concurrent.Counter with the number of requests to await (before you execute them), and the callback to execute when they have finished. Before each of the asynchronous functions returns, have it call the decrement() method of the counter; once the counter has been decremented the number of times specified, the callback will be executed:
// Ensure the existence of the "Concurrent" namespace
var Concurrent = Concurrent || {};
/**
* Constructs a new Concurrent.Counter which executes a callback once a given number of threads have
* returned. Each Concurrent.Counter instance is designed to be used only once, and then disposed of,
* so a new one should be instantiated each additional time one is needed.
*
* #param {function} callback The callback to execute once all the threads have returned
* #param {number} count The number of threads to await termination before executing the callback
*/
Concurrent.Counter = function(callback, count) {
/**
* Decrements the thread count, and executes the callback once the count reaches zero.
*/
this.decrement = function() {
if (!(-- count)) {
callback();
}
};
};
// The number of asynchronous requests to execute
var requests = 10,
// Executes a callback once all the request tasks have returned
counter = new Concurrent.Counter(function() {
// this will be executed once the tasks have completed
}, requests),
// Tracks the number of requests made
i;
for (i = 0; i < requests; i ++) {
setTimeout(function() {
/*
* Perform an asynchronous task
*/
// Decrement the counter
counter.decrement();
}, 0);
}
This is written off the top of my head, but it should work.
function createCoordinator(onFinish) {
var count = 0;
return function (callback) {
count++;
return function () {
if (callback.apply(this, arguments))
count--;
if (count == 0)
onFinish();
}
}
}
var coordinate = createCoordinator(function () { alert('done!') });
// Assume sendAJAX = function (url, onreadystatechange)
sendAJAX('url1', coordinate(function () {
if (this.readyState != 4)
return false; // Return false if not done
alert('Done with url1!');
return true;
}));
sendAJAX('url2', coordinate(function () {
if (this.readyState != 4)
return false; // Return false if not done
alert('Done with url2!');
return true;
}));