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