Fixed promises and its race conditions
This commit is contained in:
parent
e1ae4eaa0d
commit
455ae2b881
18 changed files with 113 additions and 59 deletions
|
@ -86,7 +86,9 @@ App.Auth = function(_, jQuery, util, api, appState, promise) {
|
|||
return promise.make(function(resolve, reject) {
|
||||
jQuery.removeCookie('auth');
|
||||
appState.set('loginToken', null);
|
||||
return loginAnonymous().then(resolve).fail(reject);
|
||||
return promise.wait(loginAnonymous())
|
||||
.then(resolve)
|
||||
.fail(reject);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,9 @@ App.BrowsingSettings = function(
|
|||
return promise.make(function(resolve, reject) {
|
||||
saveToLocalStorage();
|
||||
if (auth.isLoggedIn()) {
|
||||
saveToUser(auth.getCurrentUser()).then(resolve).fail(reject);
|
||||
promise.wait(saveToUser(auth.getCurrentUser()))
|
||||
.then(resolve)
|
||||
.fail(reject);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
|
|
|
@ -55,20 +55,19 @@ App.Pager = function(
|
|||
|
||||
function retrieve() {
|
||||
return promise.make(function(resolve, reject) {
|
||||
promise.wait(
|
||||
api.get(url, _.extend({}, searchParams, {page: pageNumber})))
|
||||
.then(function(response) {
|
||||
var pageSize = response.json.pageSize;
|
||||
var totalRecords = response.json.totalRecords;
|
||||
totalPages = Math.ceil(totalRecords / pageSize);
|
||||
promise.wait(api.get(url, _.extend({}, searchParams, {page: pageNumber})))
|
||||
.then(function(response) {
|
||||
var pageSize = response.json.pageSize;
|
||||
var totalRecords = response.json.totalRecords;
|
||||
totalPages = Math.ceil(totalRecords / pageSize);
|
||||
|
||||
resolve({
|
||||
entities: response.json.data,
|
||||
totalRecords: totalRecords});
|
||||
resolve({
|
||||
entities: response.json.data,
|
||||
totalRecords: totalRecords});
|
||||
|
||||
}).fail(function(response) {
|
||||
reject(response);
|
||||
});
|
||||
}).fail(function(response) {
|
||||
reject(response);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,9 @@ App.PresenterManager = function(jQuery, topNavigationPresenter, keyboard) {
|
|||
}, 100);
|
||||
|
||||
if (lastContentPresenter === null || lastContentPresenter.name !== presenter.name) {
|
||||
if (lastContentPresenter !== null && lastContentPresenter.deinit) {
|
||||
lastContentPresenter.deinit();
|
||||
}
|
||||
keyboard.reset();
|
||||
topNavigationPresenter.changeTitle(null);
|
||||
topNavigationPresenter.focus();
|
||||
|
|
|
@ -21,7 +21,7 @@ App.Presenters.HomePresenter = function(
|
|||
topNavigationPresenter.select('home');
|
||||
topNavigationPresenter.changeTitle('Home');
|
||||
|
||||
promise.waitAll(
|
||||
promise.wait(
|
||||
util.promiseTemplate('home'),
|
||||
util.promiseTemplate('post-content'),
|
||||
api.get('/globals'),
|
||||
|
|
|
@ -58,7 +58,7 @@ App.Presenters.LoginPresenter = function(
|
|||
return false;
|
||||
}
|
||||
|
||||
auth.loginFromCredentials(userNameOrEmail, password, remember)
|
||||
promise.wait(auth.loginFromCredentials(userNameOrEmail, password, remember))
|
||||
.then(function(response) {
|
||||
finishLogin();
|
||||
}).fail(function(response) {
|
||||
|
|
|
@ -52,7 +52,7 @@ App.Presenters.PagerPresenter = function(
|
|||
pager.setSearchParams(args.searchParams);
|
||||
pager.setPage(args.page || 1);
|
||||
|
||||
retrieve()
|
||||
promise.wait(retrieve())
|
||||
.then(loaded)
|
||||
.fail(loaded);
|
||||
|
||||
|
@ -62,6 +62,11 @@ App.Presenters.PagerPresenter = function(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function deinit() {
|
||||
detachNextPageLoader();
|
||||
}
|
||||
|
||||
function prevPage() {
|
||||
pager.prevPage();
|
||||
syncUrl();
|
||||
|
@ -126,7 +131,7 @@ App.Presenters.PagerPresenter = function(
|
|||
showSpinner();
|
||||
|
||||
return promise.make(function(resolve, reject) {
|
||||
pager.retrieve()
|
||||
promise.wait(pager.retrieve())
|
||||
.then(function(response) {
|
||||
updateCallback(response, forceClear || !endlessScroll);
|
||||
forceClear = false;
|
||||
|
@ -176,6 +181,10 @@ App.Presenters.PagerPresenter = function(
|
|||
}, 100);
|
||||
}
|
||||
|
||||
function detachNextPageLoader() {
|
||||
window.clearInterval(scrollInterval);
|
||||
}
|
||||
|
||||
function showPageList() {
|
||||
$pageList.show();
|
||||
}
|
||||
|
@ -224,6 +233,7 @@ App.Presenters.PagerPresenter = function(
|
|||
return {
|
||||
init: init,
|
||||
reinit: reinit,
|
||||
deinit: deinit,
|
||||
setPage: setPage,
|
||||
setSearchParams: setSearchParams,
|
||||
};
|
||||
|
|
|
@ -25,7 +25,7 @@ App.Presenters.PostListPresenter = function(
|
|||
topNavigationPresenter.changeTitle('Posts');
|
||||
searchArgs = util.parseComplexRouteArgs(args.searchArgs);
|
||||
|
||||
promise.waitAll(
|
||||
promise.wait(
|
||||
util.promiseTemplate('post-list'),
|
||||
util.promiseTemplate('post-list-item'))
|
||||
.then(function(listHtml, itemHtml) {
|
||||
|
@ -60,6 +60,10 @@ App.Presenters.PostListPresenter = function(
|
|||
order: searchArgs.order}});
|
||||
}
|
||||
|
||||
function deinit() {
|
||||
pagerPresenter.deinit();
|
||||
}
|
||||
|
||||
function render() {
|
||||
$el.html(listTemplate());
|
||||
$searchInput = $el.find('input[name=query]');
|
||||
|
@ -107,6 +111,7 @@ App.Presenters.PostListPresenter = function(
|
|||
return {
|
||||
init: init,
|
||||
reinit: reinit,
|
||||
deinit: deinit,
|
||||
render: render,
|
||||
};
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ App.Presenters.PostPresenter = function(
|
|||
editPrivileges.canChangeThumbnail = auth.hasPrivilege(auth.privileges.changePostThumbnail);
|
||||
editPrivileges.canChangeRelations = auth.hasPrivilege(auth.privileges.changePostRelations);
|
||||
|
||||
promise.waitAll(
|
||||
promise.wait(
|
||||
util.promiseTemplate('post'),
|
||||
util.promiseTemplate('post-edit'),
|
||||
util.promiseTemplate('post-content'),
|
||||
|
@ -74,7 +74,7 @@ App.Presenters.PostPresenter = function(
|
|||
function reinit(args, loaded) {
|
||||
postNameOrId = args.postNameOrId;
|
||||
|
||||
refreshPost()
|
||||
promise.wait(refreshPost())
|
||||
.then(function() {
|
||||
topNavigationPresenter.changeTitle('@' + post.id);
|
||||
render();
|
||||
|
@ -84,7 +84,7 @@ App.Presenters.PostPresenter = function(
|
|||
|
||||
function refreshPost() {
|
||||
return promise.make(function(resolve, reject) {
|
||||
promise.waitAll(api.get('/posts/' + postNameOrId))
|
||||
promise.wait(api.get('/posts/' + postNameOrId))
|
||||
.then(function(postResponse) {
|
||||
post = postResponse.json;
|
||||
postScore = postResponse.json.ownScore;
|
||||
|
@ -170,7 +170,7 @@ App.Presenters.PostPresenter = function(
|
|||
}
|
||||
|
||||
function deletePost() {
|
||||
api.delete('/posts/' + post.id)
|
||||
promise.wait(api.delete('/posts/' + post.id))
|
||||
.then(function(response) {
|
||||
router.navigate('#/posts');
|
||||
}).fail(showGenericError);
|
||||
|
@ -185,11 +185,10 @@ App.Presenters.PostPresenter = function(
|
|||
}
|
||||
|
||||
function featurePost() {
|
||||
api.post('/posts/' + post.id + '/feature')
|
||||
promise.wait(api.post('/posts/' + post.id + '/feature'))
|
||||
.then(function(response) {
|
||||
router.navigate('#/home');
|
||||
})
|
||||
.fail(showGenericError);
|
||||
}).fail(showGenericError);
|
||||
}
|
||||
|
||||
function editButtonClicked(e) {
|
||||
|
@ -304,19 +303,17 @@ App.Presenters.PostPresenter = function(
|
|||
}
|
||||
|
||||
function addFavorite() {
|
||||
api.post('/posts/' + post.id + '/favorites')
|
||||
promise.wait(api.post('/posts/' + post.id + '/favorites'))
|
||||
.then(function(response) {
|
||||
refreshPost().then(renderSidebar);
|
||||
})
|
||||
.fail(showGenericError);
|
||||
promise.wait(refreshPost()).then(renderSidebar);
|
||||
}).fail(showGenericError);
|
||||
}
|
||||
|
||||
function deleteFavorite() {
|
||||
api.delete('/posts/' + post.id + '/favorites')
|
||||
promise.wait(api.delete('/posts/' + post.id + '/favorites'))
|
||||
.then(function(response) {
|
||||
refreshPost().then(renderSidebar);
|
||||
})
|
||||
.fail(showGenericError);
|
||||
promise.wait(refreshPost()).then(renderSidebar);
|
||||
}).fail(showGenericError);
|
||||
}
|
||||
|
||||
function scoreUpButtonClicked(e) {
|
||||
|
@ -332,11 +329,10 @@ App.Presenters.PostPresenter = function(
|
|||
}
|
||||
|
||||
function score(scoreValue) {
|
||||
api.post('/posts/' + post.id + '/score', {score: scoreValue})
|
||||
promise.wait(api.post('/posts/' + post.id + '/score', {score: scoreValue}))
|
||||
.then(function() {
|
||||
refreshPost().then(renderSidebar);
|
||||
})
|
||||
.fail(showGenericError);
|
||||
promise.wait(refreshPost()).then(renderSidebar);
|
||||
}).fail(showGenericError);
|
||||
}
|
||||
|
||||
function showEditError(response) {
|
||||
|
|
|
@ -47,7 +47,7 @@ App.Presenters.RegistrationPresenter = function(
|
|||
return;
|
||||
}
|
||||
|
||||
api.post('/users', formData)
|
||||
promise.wait(api.post('/users', formData))
|
||||
.then(function(response) {
|
||||
registrationSuccess(response);
|
||||
}).fail(function(response) {
|
||||
|
|
|
@ -54,7 +54,7 @@ App.Presenters.UserAccountRemovalPresenter = function(
|
|||
messagePresenter.showError($messages, 'Must confirm to proceed.');
|
||||
return;
|
||||
}
|
||||
api.delete('/users/' + user.name)
|
||||
promise.wait(api.delete('/users/' + user.name))
|
||||
.then(function() {
|
||||
auth.logout();
|
||||
var $messageDiv = messagePresenter.showInfo($messages, 'Account deleted. <a href="">Back to main page</a>');
|
||||
|
|
|
@ -120,7 +120,7 @@ App.Presenters.UserAccountSettingsPresenter = function(
|
|||
delete formData.passwordConfirmation;
|
||||
}
|
||||
|
||||
api.put('/users/' + user.name, formData)
|
||||
promise.wait(api.put('/users/' + user.name, formData))
|
||||
.then(function(response) {
|
||||
editSuccess(response);
|
||||
}).fail(function(response) {
|
||||
|
|
|
@ -70,7 +70,7 @@ App.Presenters.UserActivationPresenter = function(
|
|||
'/password-reset/' + userNameOrEmail :
|
||||
'/activation/' + userNameOrEmail;
|
||||
|
||||
api.post(url)
|
||||
promise.wait(api.post(url))
|
||||
.then(function(response) {
|
||||
var message = operation === 'passwordReset' ?
|
||||
'Password reset request sent.' :
|
||||
|
@ -92,7 +92,7 @@ App.Presenters.UserActivationPresenter = function(
|
|||
'/finish-password-reset/' + token :
|
||||
'/finish-activation/' + token;
|
||||
|
||||
api.post(url)
|
||||
promise.wait(api.post(url))
|
||||
.then(function(response) {
|
||||
var message = operation === 'passwordReset' ?
|
||||
'Your new password is <strong>' + response.json.newPassword + '</strong>.' :
|
||||
|
|
|
@ -51,7 +51,7 @@ App.Presenters.UserBrowsingSettingsPresenter = function(
|
|||
},
|
||||
};
|
||||
|
||||
browsingSettings.setSettings(newSettings)
|
||||
promise.wait(browsingSettings.setSettings(newSettings))
|
||||
.then(function() {
|
||||
messagePresenter.showInfo($messages, 'Browsing settings updated!');
|
||||
});
|
||||
|
|
|
@ -18,7 +18,7 @@ App.Presenters.UserListPresenter = function(
|
|||
topNavigationPresenter.select('users');
|
||||
topNavigationPresenter.changeTitle('Users');
|
||||
|
||||
promise.waitAll(
|
||||
promise.wait(
|
||||
util.promiseTemplate('user-list'),
|
||||
util.promiseTemplate('user-list-item'))
|
||||
.then(function(listHtml, itemHtml) {
|
||||
|
@ -55,6 +55,10 @@ App.Presenters.UserListPresenter = function(
|
|||
order: searchArgs.order}});
|
||||
}
|
||||
|
||||
function deinit() {
|
||||
pagerPresenter.deinit();
|
||||
}
|
||||
|
||||
function render() {
|
||||
$el.html(listTemplate());
|
||||
$el.find('.order a').click(orderLinkClicked);
|
||||
|
@ -93,6 +97,7 @@ App.Presenters.UserListPresenter = function(
|
|||
return {
|
||||
init: init,
|
||||
reinit: reinit,
|
||||
deinit: deinit,
|
||||
render: render,
|
||||
};
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ App.Presenters.UserPresenter = function(
|
|||
topNavigationPresenter.select(auth.isLoggedIn(userName) ? 'my-account' : 'users');
|
||||
topNavigationPresenter.changeTitle(userName);
|
||||
|
||||
promise.waitAll(
|
||||
promise.wait(
|
||||
util.promiseTemplate('user'),
|
||||
api.get('/users/' + userName))
|
||||
.then(function(
|
||||
|
|
|
@ -1,28 +1,57 @@
|
|||
var App = App || {};
|
||||
|
||||
App.Promise = function(jQuery) {
|
||||
App.Promise = function(_, jQuery) {
|
||||
|
||||
function make(callback)
|
||||
{
|
||||
var active = [];
|
||||
|
||||
function make(callback) {
|
||||
var deferred = jQuery.Deferred();
|
||||
callback(deferred.resolve, deferred.reject);
|
||||
return deferred.promise();
|
||||
var promise = deferred.promise();
|
||||
|
||||
callback(function() {
|
||||
deferred.resolve.apply(deferred, arguments);
|
||||
active = _.without(active, promise);
|
||||
}, function() {
|
||||
deferred.reject.apply(deferred, arguments);
|
||||
active = _.without(active, promise);
|
||||
});
|
||||
|
||||
promise.then(function() {
|
||||
if (!_.contains(active, promise)) {
|
||||
throw new Error('Broken promise');
|
||||
}
|
||||
});
|
||||
|
||||
active.push(promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
function wait(promise) {
|
||||
return jQuery.when(promise);
|
||||
function wait() {
|
||||
var promises = arguments;
|
||||
var deferred = jQuery.Deferred();
|
||||
return jQuery.when.apply(jQuery, promises)
|
||||
.then(function() {
|
||||
return deferred.resolve.apply(deferred, arguments);
|
||||
}).fail(function() {
|
||||
return deferred.reject.apply(deferred, arguments);
|
||||
});
|
||||
}
|
||||
|
||||
function waitAll() {
|
||||
return jQuery.when.apply(jQuery, arguments);
|
||||
function abortAll() {
|
||||
active = [];
|
||||
}
|
||||
|
||||
function getActive() {
|
||||
return active.length;
|
||||
}
|
||||
|
||||
return {
|
||||
make: make,
|
||||
wait: wait,
|
||||
waitAll: waitAll,
|
||||
getActive: getActive,
|
||||
abortAll: abortAll,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
App.DI.registerSingleton('promise', ['jQuery'], App.Promise);
|
||||
App.DI.registerSingleton('promise', ['_', 'jQuery'], App.Promise);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var App = App || {};
|
||||
|
||||
App.Router = function(pathJs, _, jQuery, util, appState, presenterManager) {
|
||||
App.Router = function(pathJs, _, jQuery, promise, util, appState, presenterManager) {
|
||||
|
||||
var root = '#/';
|
||||
var previousLocation = window.location.href;
|
||||
|
@ -65,6 +65,9 @@ App.Router = function(pathJs, _, jQuery, util, appState, presenterManager) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
//abort every operation that can be executed
|
||||
promise.abortAll();
|
||||
previousLocation = window.location.href;
|
||||
|
||||
var finalParams = _.extend(
|
||||
|
@ -87,4 +90,4 @@ App.Router = function(pathJs, _, jQuery, util, appState, presenterManager) {
|
|||
|
||||
};
|
||||
|
||||
App.DI.registerSingleton('router', ['pathJs', '_', 'jQuery', 'util', 'appState', 'presenterManager'], App.Router);
|
||||
App.DI.registerSingleton('router', ['pathJs', '_', 'jQuery', 'promise', 'util', 'appState', 'presenterManager'], App.Router);
|
||||
|
|
Loading…
Reference in a new issue