I need a initialization function called only one time for a module. This function is a promise and is called by an execute function. If execute is called twice, the second must wait the initialization then continue the execution.
I wrote this code, but the second call of execute is always waiting and doesn't never return. What have i missed?
var initialized = false;
var initializing = false;
var initializationPromise;
var init = function () {
initializing = true;
return q.promise(function (resolve) {
// simulate initialization
setTimeout(function () {
// initialized
initialized = true;
resolve();
}, 1000);
}).fin(function () {
initializing = false;
});
};
var execute = function () {
return q.promise(function (resolve, reject, notify) {
if (initialized) {
// already initialized
resolve();
} else {
if (!initializing) {
// initializing
initializationPromise = init().then(function () {
// simulate execution
setTimeout(function () {
resolve();
}, 1000);
}, function (reason) {
reject(reason);
});
} else {
// Wait : initializing in progress
return initializationPromise;
}
}
});
};
execute().then(function () {
// This is executed
});
execute().then(function () {
// This is never executed
});
// Wait : initializing in progress
return initializationPromise;
is not correct. That doesn't wait for anything, it just drops out of the q.promise constructor and does not do anything. Also you seem to employ the Promise constructor antipattern.
What you should do instead is
var initialisationPromise = null;
function isInitialised() {
return initialisationPromise != null && initialisationPromise.isFulfilled();
}
function isInitialising() {
return initialisationPromise != null && initialisationPromise.isPending();
}
function init() {
// init can be called as often as necessary, and returns when it's done
if (initialisationPromise == null) { // do the test here!
// this part runs only once
initialisationPromise = q.promise(function (resolve) {
// simulate initialization
setTimeout(function () {
// initialized
resolve();
}, 1000);
});
}
return initialisationPromise;
}
function execute() {
return init().then(function () {
return q.promise(function(resolve, reject, notify) {
// simulate execution
setTimeout(function () {
resolve();
}, 1000);
});
});
}
A resolved/rejected promise will maintain its state (resolved or rejected state), so you can use it to run the initialization code only once. To do that, the init() function should return always the same promise and not create it every time.
For this reason, we create a deferred object (initializationDeferred) outside the init() method and use initializationDeferred to return the same promise every time init() method is called. We need, also, to check if the init() has been already done before, we use the shared variable initializationStarted to skip the setTimeout if already done in a previous invocation.
Now, inside execute you can be sure that the onFulfilled callback of then() is called only when init() method is initialized.
var initializationDeferred = Q.defer(); // Create here the deferred object so it's common to all init() invocations
var initializationStarted = false;
var init = function() {
if (!initializationStarted) {
initializationStarted = true;
setTimeout(function() {
// initialized
console.log('Init timeout fired!');
initializationDeferred.resolve(true); // Resolve the promise associated to the deferred object
}, 1000);
}
return initializationDeferred.promise; // Return the promise associated to the deferred object
};
var execute = function() {
return init().then(function(initialized) {
// Here your module is initialized and you can do whatever you want
// The value of "initialized" here is always "true"
console.log('Execute: initialized?', initialized);
});
};
execute().then(function() {
// This is executed
console.log('Execute First invocation');
});
execute().then(function() {
// This is executed too
console.log('Execute Second invocation');
});
<script src="http://cdnjs.cloudflare.com/ajax/libs/q.js/0.9.2/q.js"></script>
Related
I would like to execute functions one at a time and call another function when a function is finished. I was able to do it using callbacks but not using a promise chain.
Here is what I tried (based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#chained_promises) but it executes the first 3 functions at the same time instead of waiting 1 second in each function:
function displayAll() {
var myPromise = (new Promise(display1))
.then((new Promise(display2))
.then((new Promise(display3))
.then(display4)));
}
function display1(resolve) {
setTimeout(function () {
console.log("display1");
resolve();
}, 1000);
}
function display2(resolve) {
setTimeout(function () {
console.log("display2");
resolve();
}, 1000);
}
function display3(resolve) {
setTimeout(function () {
console.log("display3");
resolve();
}, 1000);
}
function display4(resolve) {
setTimeout(function () {
console.log("display4");
}, 1000);
}
Do you know what is wrong with the code and if it is possible to do what I am trying to do without callbacks?
In order to chain Promises (MDN) you need to return a promise in then method callback, instead you are constructing the promise as an argument of the then method.
This will trigger the Promise as soon as is "encountered" the new keyword, that is not the expected behaviour. You instead want to wait the first Promise to end, and then chain the then method that will create a new Promise:
function displayAll() {
var myPromise = (new Promise(display1))
// .then(new Promise(display2)) <-- you are calling here the promise
.then(function() {
return new Promise(display2) // <-- here we return a promise to chain
})
.then(()=> new Promise(display3)) // same with arrow functions
.then(display4);
}
From your code:
function displayAll() {
var myPromise = (new Promise(display1))
.then(()=> new Promise(display2))
.then(() => new Promise(display3))
.then(display4);
}
function display1(resolve) {
setTimeout(function () {
console.log("display1");
resolve();
}, 1000);
}
function display2(resolve) {
setTimeout(function () {
console.log("display2");
resolve();
}, 1000);
}
function display3(resolve) {
setTimeout(function () {
console.log("display3");
resolve();
}, 1000);
}
function display4(resolve) {
setTimeout(function () {
console.log("display4");
}, 1000);
}
displayAll()
Another more clear approach:
You can also make your display functions return a Promise such that you can pass them directly to the then method:
function display1() {
return new Promise(resolve => {
setTimeout(function () {
console.log("display1");
resolve();
}, 1000);
});
}
function display2() {
return new Promise(resolve => {
setTimeout(function () {
console.log("display2");
resolve();
}, 1000);
});
}
function display3() {
return new Promise(resolve => {
setTimeout(function () {
console.log("display3");
resolve();
}, 1000);
});
}
function display4() {
return new Promise(resolve => {
setTimeout(function () {
console.log("display4");
resolve();
}, 1000);
});
}
let myPromise =
display1()
.then(display2)
.then(display3)
.then(display4)
A walk through of displayAll's order of execution:
var myPromise = (new Promise(display1))
The Promise constructor calls display1 which sets up a timeout to log "display1" and resolve the promise. This works perfectly and the initial 1 second delay is honored.
.then(new Promise(display2))
calls the then method of myPromise during execution of displayAll.
The argument for then is evaluated before the making the call .
Creating then's promise argument causes the Promise constructor to call display2 which sets up a timeout relative to displayAll's time-of-execution.
When called then silently ignores the promise argument because it's not a function. Default handlers, used by then in the absence of callable arguments, pass through incoming data or promise rejection reasons.
.then(new Promise(display3))
operates the same as the previous then clause: set up a timer relative to displayAll's time-of-execution and use default handlers which pass through data or rejection reasons.
.then(display4)));
registers display4 as a handler to be called when the promise returned in step 3 becomes fullfilled. display4 sets up a workable timer to log "display4". Note display4's argument is now misnamed - the argument passed to successive fulfillment handlers is the value returned by the previous promise handler in the chain.
The expected output of calling displayAll is then to
Delay a second after displayAll was called and log "display1".
Delay a second, again after displayAll was called, and log "display2".
Delay a second, again after displayAll was called, and log "display3".
When promise chain handling executes display4 as a handler, set up a timer to log "display4" - so it logs one second after "display3".
One solution to stagger the execution of the display functions would be to move promise creation from where then arguments are calculated into the handler itself. The handler can return the promise to delay proceeding down the promise chain until the returned promise is resolved by the timer:
function displayN() {
return new Promise( resolve =>
setTimer( function() {
console.log("displayN");
resolve();
}, 1000)
);
}
Other solutions and approaches are possible. Promisifying setTimeout to create a promise that resolves after a specified interval can be useful for delaying steps in a promise chain or async function. As a fairly minimal example:
function delayPromise( msec) {
return new Promise(resolve=> setTimeout( resolve, msec));
}
As an aside only, the value of a promise chain is the promise returned by the last then, catch or finally call in the chain - promise chain values may be returned by a function but in practice at least are rarely recorded in a variable.
I need a simple debounce function with immediate always true.
Without resorting to lodash and with the help of Can someone explain the "debounce" function in Javascript , I implemented it as following,
function debounce(func, wait) {
var timeout;
return function() {
if (!timeout) func.apply(this, arguments);
clearTimeout(timeout);
timeout = setTimeout(()=>{timeout = null}, wait);
};
};
It works as expected until I need to debounce axios request. Assumed I have a debounced axios method, I would like the calling method to be as usual, which means my debounced axios method should return promise I believe.
//the calling method should not change
debounced_axios().then(res => {...}).catch(err => {...})
The essence of original debounce implementation is to just run func once in a wait timeframe, but how do I just return one promise in a wait timeframe ?
Then I came up with the following solution
all_timers = {}
function debounce_axios(input, wait) {
return new Promise((resolve, reject) => {
let timer = all_timers.[input] //check if it is a repeated request, pseudo code
if (!timer) {
axios(input).then(res=>{
resolve(res)
}).catch(err => {
reject(err)
})
}
clearTimeout(timer);
timer = setTimeout(()=>{timer = null}, wait);
all_timers[input] = timer
};
};
So the essence of my debounce_axios is to let the promise stay in pending state for the repeated request.Then the calling method debounced_axios().then(res => {...}).catch(err => {...}) does not need to change.
The answer here Are JavaScript forever-pending promises bad? said "There should be no side effect."
But I am still not 100% sure about letting a promise stay in pending forever.
The other question is that Promise Anti patterns suggested not creating unnecessary promise. But in my case creating a new promise seems necessary.
In a nutshell is there a simple to way to debounce axios request (or any request returns promise) ?
But I am still not 100% sure about letting a promise stay in pending forever.
I agree that it's not a good idea. A better approach would be to move the entire promise chain inside the debounced function.
Another option would be to return a cached value when the debounced call does not trigger a new request. This would solve your problem that you always need to return a promise:
function debounce(func, wait) {
var timeout, value;
return function() {
if (!timeout) value = func.apply(this, arguments);
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = value = null;
}, wait);
return value;
};
}
Of course that would mean that in some cases, multiple then handlers will be called when your request finishes. It depends on your application whether that is a problem or just superfluous work.
The other question is that Promise Anti patterns suggested not creating unnecessary promise. But in my case creating a new promise seems necessary.
Only one promise is necessary: when you create the never-resolved one. You can write that as
function debounce(func, wait) {
var timeout;
const never = new Promise(resolve => {/* do nothing*/});
return function() {
const result = timeout ? never : func.apply(this, arguments);
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
}, wait);
return result;
};
}
Or at least avoid the .then(resolve).catch(reject) part. Better write
function debounce(func, wait) {
var timeout;
return function() {
return new Promise(resolve => {
if (!timeout) resolve(func.apply(this, arguments));
// ^^^^^^^
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
}, wait);
});
};
}
And should you consider to reject the promise in case that the timeout has not yet occurred (so that the calling code can handle the rejection), you don't need new Promise either:
function debounce(func, wait) {
var timeout;
return function() {
const result = timeout
? Promise.reject(new Error("called during debounce period"))
: Promise.resolve(func.apply(this, arguments));
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
}, wait);
return result;
};
}
In essence, you need to share result of your debounce function. In your case, thats a promise:
const debouncedGetData = debounce(getData, 500)
let promiseCount = 0
let resultCount = 0
test()
function test() {
console.log('start')
callDebouncedThreeTimes()
setTimeout(callDebouncedThreeTimes, 200)
setTimeout(callDebouncedThreeTimes, 900)
}
function callDebouncedThreeTimes () {
for (let i=0; i<3; i++) {
debouncedGetData().then(r => {
console.log('Result count:', ++resultCount)
console.log('r', r)
})
}
}
function debounce(func, wait) {
let waiting;
let sharedResult;
return function() {
// first call will create the promise|value here
if (!waiting) {
setTimeout(clearWait, wait)
waiting = true
sharedResult = func.apply(this, arguments);
}
// else new calls within waitTime will be discarded but shared the result from first call
function clearWait() {
waiting = null
sharedResult = null
}
return sharedResult
};
}
function getData () {
console.log('Promise count:', ++promiseCount)
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(666)
}, 1000)
})
}
In the Master.aspx I have some init code
app.asynInit(
getData();// this needs to execute first
)
And then in one of the custom control I have some logic
doSomethingLater() // this needs to wait for the getData()
I am not sure what is the best way to make sure doSomething() always execute after app.asynInit()
My current solution is to use jQuery's custom event.
app.asynInit() // this needs to execute first
$(document).trigger('initCompleted')
$(document).on('initCompleted',function(){ document() // this needs to wait for the getData()})
It appears to work. But is there any better alternatives? Should I use document here?
You can try to use Promise class:
var initAsync = function () {
var promise = new Promise(function (done) {
setTimeout(function () {
console.log('DONE');
done();
}, 3000);
});
return promise;
};
(async function () {
await initAsync();
console.log('continue...');
// doSomething();
}());
Source: Promise
If you're using this action on IE old version. You can use callback function:
var init = function (callback) {
setTimeout(function () {
console.log('DONE');
callback();
}, 3000);
};
init(function () {
console.log('continue...');
// doSomething();
});
I end up in with this implementation with the callback approach:
In Master.aspx:
app.asynInit(
getData();// this needs to execute first
window.isDataGet = true;
)
function onDataGet(cb){
if(window.isDataGet )
cb();
else
setTimeout(onDataGet,100)
}
In Custom Control:
onDataGet(//callback function here)
i use angular.js in front side.
in my controller.js i defined an init() method that will be called in
init of my controller.
Init method definition:
var init = function () {
$scope.callTeamsService();
if ($scope.teams.length == 0){
....
}else{
...
}
.....
};
in $scope.callTeamsService i filled in $scope.teams variable.
$scope.callTeamsService method definition:
$scope.callTeamsService = function(){
NavService.getTeams(function (response) {
$timeout(function () {
$scope.teams = response;
}
}, 200);
});
};
In my service.js i defined a getTeams method as follow:
service.getEquipes = function (callback) {
$http.get(urlBase+'users/' + $rootScope.globals.currentUser.loggedUser.idUser + '/teams')
.success(function (response) {
callback(response);
});
};
My problem is when $scope.teams.length == 0 condition is reached the
service.getEquipes method in my service.js is not yet called.
How can i modify this code in order to finish the execution of $scope.callTeamsService method before reaching $scope.teams.length == 0 condition.
service/factory
service.getEquipes = function () {
return $http.get(urlBase+'users/' + $rootScope.globals.currentUser.loggedUser.idUser + '/teams');
};
// controller
var promise = NavService.getTeams.then (
function(data) {
//assign to $scope or do logic
},
function(err){
console.log(err)
}
)
How can i modify this code in order to finish the execution of $scope.callTeamsService method before reaching $scope.teams.length == 0 condition.
That's the wrong way round - you need to wait with executing the $scope.teams.length == 0 condition until the $scope.callTeamsService method has finished.
The classical method would be to give the $scope.callTeamsService method a callback parameter and call that in the timeout instead of $scope.teams = response;. Then you can put your condition in the init function in the callback that you pass.
However, you seem to want to use promises. For that, all of your functions (that all are asynchronous) should return a promise:
service.getEquipes = function (callback) {
return $http.get(urlBase+'users/' + $rootScope.globals.currentUser.loggedUser.idUser + '/teams');
}
(that was easy, $http already returns promises)
$scope.callTeamsService = function() {
return NavService.getTeams().then(function(teams) {
return $timeout(function() {
return teams;
}, 200);
});
};
(and $timeout does as well - by invoking then and returning it from the callback you can chain them and get a new promise for both)
function init() {
return $scope.callTeamsService().then(function(teams) {
$scope.teams = teams;
if (teams.length == 0) {
…
} else {
…
}
});
}
I have a web app which must call the server multiple times. So far, I had a long nested callback chain; but I would like to use jQuery's when,then etc. functionality. However, I can't seem to get stuff running again after using a then.
$
.when ($.get('pages/run-tool.html'))
.then (function (args)
{
// This works fine
alert(args);
$('#content').replaceWith (args);
$('#progress-bar').progressbar ({value: 0});
})
.then ($.get('pages/test.html'))
.done (function(args)
{
// This prints the same as the last call
alert (args);
});
What am I doing wrong? I guess its some scoping issue, as I can see the second get call being executed. Using two different args variables does not help as the argument passed to the done function is still the first get request.
As an update:
With modern jquery (1.8+) you don't need the preliminary when because get returns a Deferred Promise.
Also, pipe is deprecated. Use then instead. Just be sure to return the result of the new get which becomes the Promise attached to by subsequent then/*done*/fail calls.
So:
$.get('pages/run-tool.html')
.then (function (args) { // this will run if the above .get succeeds
// This works fine
alert(args);
$('#content').replaceWith (args);
$('#progress-bar').progressbar ({value: 0});
})
.then (function() { // this will run after the above then-handler (assuming it ran)
return $.get('pages/test.html'); // the return value creates a new Deferred object
})
.done (function(args) { // this will run after the second .get succeeds (assuming it ran)
alert (args);
});
All three callbacks (the two with then and the one with done) are applied to the same request – the original when call. This is because then returns the same Deferred object, rather than a new one, so that you can add multiple event handlers.
You need to use pipe instead.
$
.when ($.get('pages/run-tool.html'))
.then (function (args)
{
// This works fine
alert(args);
$('#content').replaceWith (args);
$('#progress-bar').progressbar ({value: 0});
})
.pipe (function() {
return $.get('pages/test.html'); // the return value creates a new Deferred object
})
.done (function(args)
{
alert (args);
});
Here is an wonderfully simple and highly effective AJAX chaining / queue plugin. It will execute you ajax methods in sequence one after each other.
It works by accepting an array of methods and then executing them in sequence. It wont execute the next method whilst waiting for a response.
//--- THIS PART IS YOUR CODE -----------------------
$(document).ready(function () {
var AjaxQ = [];
AjaxQ[0] = function () { AjaxMethod1(); }
AjaxQ[1] = function () { AjaxMethod2(); }
AjaxQ[3] = function () { AjaxMethod3(); }
//Execute methods in sequence
$(document).sc_ExecuteAjaxQ({ fx: AjaxQ });
});
//--- THIS PART IS THE AJAX PLUGIN -------------------
$.fn.sc_ExecuteAjaxQ = function (options) {
//? Executes a series of AJAX methods in dequence
var options = $.extend({
fx: [] //function1 () { }, function2 () { }, function3 () { }
}, options);
if (options.fx.length > 0) {
var i = 0;
$(this).unbind('ajaxComplete');
$(this).ajaxComplete(function () {
i++;
if (i < options.fx.length && (typeof options.fx[i] == "function")) { options.fx[i](); }
else { $(this).unbind('ajaxComplete'); }
});
//Execute first item in queue
if (typeof options.fx[i] == "function") { options.fx[i](); }
else { $(this).unbind('ajaxComplete'); }
}
}
The answer cdr gave, which has the highest vote at the moment, is not right.
When you have functions a, b, c each returns a $.Deferred() object, and chains the functions like the following:
a().then(b).then(c)
Both b and c will run once the promise returned from a is resolved. Since both then() functions are tied to the promise of a, this works similiar to other Jquery chaining such as:
$('#id').html("<div>hello</div>").css({display:"block"})
where both html() and css() function are called on the object returned from $('#id');
So to make a, b, c run after the promise returned from the previous function is resolved, you need to do this:
a().then(function(){
b().then(c)
});
Here the call of function c is tied to the promise returned from function b.
You can test this using the following code:
function a() {
var promise = $.Deferred();
setTimeout(function() {
promise.resolve();
console.log("a");
}, 1000);
return promise;
}
function b() {
console.log("running b");
var promise = $.Deferred();
setTimeout(function () {
promise.resolve();
console.log("b");
}, 500);
return promise;
}
function c() {
console.log("running c");
var promise = $.Deferred();
setTimeout(function () {
promise.resolve();
console.log("c");
}, 1500);
return promise;
}
a().then(b).then(c);
a().then(function(){
b().then(c)
});
Change the promise in function b() from resolve() to reject() and you will see the difference.
<script type="text/javascript">
var promise1 = function () {
return new
$.Deferred(function (def) {
setTimeout(function () {
console.log("1");
def.resolve();
}, 3000);
}).promise();
};
var promise2 = function () {
return new
$.Deferred(function (def) {
setTimeout(function () {
console.log("2");
def.resolve();
}, 2000);
}).promise();
};
var promise3 = function () {
return new
$.Deferred(function (def) {
setTimeout(function () {
console.log("3");
def.resolve();
}, 1000);
}).promise();
};
var firstCall = function () {
console.log("firstCall");
$.when(promise1())
.then(function () { secondCall(); });
};
var secondCall = function () {
console.log("secondCall")
$.when(promise2()).then(function () { thirdCall(); });
};
var thirdCall = function () {
console.log("thirdCall")
$.when(promise3()).then(function () { console.log("done"); });
};
$(document).ready(function () {
firstCall();
});
</script>
I thought I would leave this little exercise here for anyone who may find it useful, we build an array of requests and when they are completed, we can fire a callback function:
var urls = [{
url: 'url1',
data: 'foo'
}, {
url: 'url2',
data: 'foo'
}, {
url: 'url3',
data: 'foo'
}, {
url: 'url4',
data: 'foo'
}];
var requests = [];
var callback = function (result) {
console.log('done!');
};
var ajaxFunction = function () {
for (var request, i = -1; request = urls[++i];) {
requests.push($.ajax({
url: request.url,
success: function (response) {
console.log('success', response);
}
}));
}
};
// using $.when.apply() we can execute a function when all the requests
// in the array have completed
$.when.apply(new ajaxFunction(), requests).done(function (result) {
callback(result)
});
My way is to apply callback function:
A(function(){
B(function(){
C()})});
where A, B can be written as
function A(callback)
$.ajax{
...
success: function(result){
...
if (callback) callback();
}
}