├── .gitignore ├── .jshintrc ├── Gruntfile.js ├── LICENSE-MIT ├── README.md ├── ajaxQueue.jquery.json ├── dist ├── jquery.ajaxQueue.js ├── jquery.ajaxQueue.min.js └── jquery.ajaxQueue.min.map ├── package.json ├── src ├── .jshintrc └── jQuery.ajaxQueue.js └── test ├── .jshintrc ├── jquery.ajaxQueue.html └── jquery.ajaxQueue_test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/.sizecache.json 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "boss": true, 11 | "eqnull": true, 12 | "node": true 13 | } 14 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(grunt) { 4 | 5 | // Project configuration. 6 | grunt.initConfig({ 7 | // Metadata. 8 | pkg: grunt.file.readJSON("package.json"), 9 | // Task configuration. 10 | concat: { 11 | options: { 12 | banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + 13 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 14 | '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' + 15 | '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + 16 | ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n' 17 | }, 18 | dist: { 19 | src: ['src/jquery.ajaxQueue.js'], 20 | dest: 'dist/jquery.ajaxQueue.js' 21 | }, 22 | }, 23 | uglify: { 24 | options: { 25 | banner: "/*! jQuery Ajax Queue v<%= pkg.version %> | (c) <%= grunt.template.today('yyyy') %> <%= pkg.author.name %> | Licensed <%= _.pluck(pkg.licenses, \"type\").join(\", \") %> */\n", 26 | sourceMap: "dist/jquery.ajaxQueue.min.map", 27 | beautify: { 28 | ascii_only: true 29 | } 30 | }, 31 | all: { 32 | files: { 33 | "dist/jquery.ajaxQueue.min.js": ['src/jquery.ajaxQueue.js'] 34 | } 35 | }, 36 | }, 37 | qunit: { 38 | files: ['test/**/*.html'] 39 | }, 40 | jshint: { 41 | gruntfile: { 42 | options: { 43 | jshintrc: '.jshintrc' 44 | }, 45 | src: ['Gruntfile.js'] 46 | }, 47 | src: { 48 | options: { 49 | jshintrc: 'src/.jshintrc' 50 | }, 51 | src: ['src/**/*.js'] 52 | }, 53 | test: { 54 | options: { 55 | jshintrc: 'test/.jshintrc' 56 | }, 57 | src: ['test/**/*.js'] 58 | }, 59 | }, 60 | watch: { 61 | gruntfile: { 62 | files: '', 63 | tasks: ['jshint:gruntfile'] 64 | }, 65 | src: { 66 | files: '', 67 | tasks: ['jshint:src', 'qunit'] 68 | }, 69 | test: { 70 | files: '', 71 | tasks: ['jshint:test', 'qunit'] 72 | }, 73 | }, 74 | }); 75 | 76 | // Default task. 77 | grunt.loadNpmTasks("grunt-contrib-concat"); 78 | grunt.loadNpmTasks("grunt-contrib-watch"); 79 | grunt.loadNpmTasks("grunt-contrib-jshint"); 80 | grunt.loadNpmTasks("grunt-contrib-uglify"); 81 | grunt.loadNpmTasks("grunt-contrib-qunit"); 82 | grunt.registerTask('default', ['jshint', 'concat', 'qunit', 'manifest', 'concat', 'uglify']); 83 | 84 | grunt.registerTask( "manifest", function() { 85 | var pkg = grunt.config( "pkg" ); 86 | grunt.file.write( "ajaxQueue.jquery.json", JSON.stringify({ 87 | name: "ajaxQueue", 88 | title: pkg.title, 89 | description: pkg.description, 90 | keywords: pkg.keywords, 91 | version: pkg.version, 92 | author: pkg.author, 93 | maintainers: pkg.maintainers, 94 | licenses: pkg.licenses.map(function( license ) { 95 | license.url = license.url.replace( "master", pkg.version ); 96 | return license; 97 | }), 98 | bugs: pkg.bugs, 99 | homepage: pkg.homepage, 100 | docs: pkg.homepage, 101 | dependencies: { 102 | jquery: ">=1.5" 103 | } 104 | }, null, "\t" ) ); 105 | }); 106 | }; 107 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Corey Frang 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jQuery Ajax Queue 2 | 3 | ## Which files to use? 4 | The release version of the code is found in the `dist/` directory. 5 | 6 | In your web page: 7 | 8 | ```html 9 | 10 | 11 | 19 | ``` 20 | 21 | ## Documentation 22 | 23 | This pluging creates a new method which ensures only one AJAX request is running at a time. It waits for the previous request(s) to finish before starting a new one using jQuery's built in queue. 24 | 25 | ### `jQuery.ajaxQueue( options )` 26 | Takes the same options as [jQuery.ajax](http://api.jquery.com/jQuery.ajax), and returns a promise. The return value is not a `jqXHR`, but it will behave like one. The `abort()` method on the returned object will remove the request from the queue if it has not begun, or pass it along to the jqXHR's abort method once the request begins. 27 | 28 | ## Examples 29 | _(Coming soon)_ 30 | 31 | ## Release History 32 | 33 | * v0.1.1 - 2013-01-16 34 | * Changed keywords in package file 35 | * v0.1.0 - 2013-01-16 36 | * Started as an [answer on Stack Overflow](http://stackoverflow.com/a/3035268/91914). 37 | 38 | ## License 39 | Copyright (c) 2013 Corey Frang 40 | Licensed under the MIT license. 41 | 42 | ## Contributing 43 | In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [grunt](https://github.com/cowboy/grunt). 44 | 45 | ### Important notes 46 | Please don't edit files in the `dist` subdirectory as they are generated via grunt. You'll find source code in the `src` subdirectory! 47 | 48 | While grunt can run the included unit tests via PhantomJS, this shouldn't be considered a substitute for the real thing. Please be sure to test the `test/*.html` unit test file(s) in _actual_ browsers. 49 | 50 | ### Installing grunt 51 | _This assumes you have [node.js](http://nodejs.org/) and [npm](http://npmjs.org/) installed already._ 52 | 53 | 1. Test that grunt is installed globally by running `grunt --version` at the command-line. 54 | 1. If grunt isn't installed globally, run `npm install -g grunt` to install the latest version. _You may need to run `sudo npm install -g grunt`._ 55 | 1. From the root directory of this project, run `npm install` to install the project's dependencies. 56 | 57 | ### Installing PhantomJS 58 | 59 | In order for the qunit task to work properly, [PhantomJS](http://www.phantomjs.org/) must be installed and in the system PATH (if you can run "phantomjs" at the command line, this task should work). 60 | 61 | Unfortunately, PhantomJS cannot be installed automatically via npm or grunt, so you need to install it yourself. There are a number of ways to install PhantomJS. 62 | 63 | * [PhantomJS and Mac OS X](http://ariya.ofilabs.com/2012/02/phantomjs-and-mac-os-x.html) 64 | * [PhantomJS Installation](http://code.google.com/p/phantomjs/wiki/Installation) (PhantomJS wiki) 65 | 66 | Note that the `phantomjs` executable needs to be in the system `PATH` for grunt to see it. 67 | 68 | * [How to set the path and environment variables in Windows](http://www.computerhope.com/issues/ch000549.htm) 69 | * [Where does $PATH get set in OS X 10.6 Snow Leopard?](http://superuser.com/questions/69130/where-does-path-get-set-in-os-x-10-6-snow-leopard) 70 | * [How do I change the PATH variable in Linux](https://www.google.com/search?q=How+do+I+change+the+PATH+variable+in+Linux) 71 | -------------------------------------------------------------------------------- /ajaxQueue.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ajaxQueue", 3 | "title": "jQuery Ajax Queue", 4 | "description": "A simple queue for ajax requests", 5 | "keywords": [ 6 | "ajax", 7 | "queue" 8 | ], 9 | "version": "0.1.2pre", 10 | "author": { 11 | "name": "Corey Frang", 12 | "email": "gnarf37@gmail.com", 13 | "url": "https://github.com/gnarf37" 14 | }, 15 | "maintainers": [ 16 | { 17 | "name": "Corey Frang", 18 | "email": "gnarf37@gmail.com", 19 | "url": "https://github.com/gnarf37" 20 | } 21 | ], 22 | "licenses": [ 23 | { 24 | "type": "MIT", 25 | "url": "https://github.com/jquery/gnarf37/jquery-ajaxQueue/blob/0.1.2pre/LICENSE-MIT" 26 | } 27 | ], 28 | "bugs": "https://github.com/gnarf37/jquery-ajaxQueue/issues", 29 | "homepage": "https://github.com/gnarf37/jquery-ajaxQueue", 30 | "docs": "https://github.com/gnarf37/jquery-ajaxQueue", 31 | "dependencies": { 32 | "jquery": ">=1.5" 33 | } 34 | } -------------------------------------------------------------------------------- /dist/jquery.ajaxQueue.js: -------------------------------------------------------------------------------- 1 | /*! jQuery Ajax Queue - v0.1.2pre - 2013-03-19 2 | * https://github.com/gnarf37/jquery-ajaxQueue 3 | * Copyright (c) 2013 Corey Frang; Licensed MIT */ 4 | (function($) { 5 | 6 | // jQuery on an empty object, we are going to use this as our Queue 7 | var ajaxQueue = $({}); 8 | 9 | $.ajaxQueue = function( ajaxOpts ) { 10 | var jqXHR, 11 | dfd = $.Deferred(), 12 | promise = dfd.promise(); 13 | 14 | // run the actual query 15 | function doRequest( next ) { 16 | jqXHR = $.ajax( ajaxOpts ); 17 | jqXHR.done( dfd.resolve ) 18 | .fail( dfd.reject ) 19 | .then( next, next ); 20 | } 21 | 22 | // queue our ajax request 23 | ajaxQueue.queue( doRequest ); 24 | 25 | // add the abort method 26 | promise.abort = function( statusText ) { 27 | 28 | // proxy abort to the jqXHR if it is active 29 | if ( jqXHR ) { 30 | return jqXHR.abort( statusText ); 31 | } 32 | 33 | // if there wasn't already a jqXHR we need to remove from queue 34 | var queue = ajaxQueue.queue(), 35 | index = $.inArray( doRequest, queue ); 36 | 37 | if ( index > -1 ) { 38 | queue.splice( index, 1 ); 39 | } 40 | 41 | // and then reject the deferred 42 | dfd.rejectWith( ajaxOpts.context || ajaxOpts, [ promise, statusText, "" ] ); 43 | return promise; 44 | }; 45 | 46 | return promise; 47 | }; 48 | 49 | })(jQuery); 50 | -------------------------------------------------------------------------------- /dist/jquery.ajaxQueue.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery Ajax Queue v0.1.2pre | (c) 2013 Corey Frang | Licensed MIT */ 2 | (function(e){var r=e({});e.ajaxQueue=function(n){function t(r){u=e.ajax(n),u.done(a.resolve).fail(a.reject).then(r,r)}var u,a=e.Deferred(),i=a.promise();return r.queue(t),i.abort=function(o){if(u)return u.abort(o);var c=r.queue(),f=e.inArray(t,c);return f>-1&&c.splice(f,1),a.rejectWith(n.context||n,[i,o,""]),i},i}})(jQuery); 3 | //@ sourceMappingURL=dist/jquery.ajaxQueue.min.map -------------------------------------------------------------------------------- /dist/jquery.ajaxQueue.min.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"dist/jquery.ajaxQueue.min.js","sources":["src/jquery.ajaxQueue.js"],"names":["$","ajaxQueue","ajaxOpts","doRequest","next","jqXHR","ajax","done","dfd","resolve","fail","reject","then","Deferred","promise","queue","abort","statusText","index","inArray","splice","rejectWith","context","jQuery"],"mappings":"CAAA,SAAUA,GAGV,GAAIC,GAAYD,KAEhBA,GAAEC,UAAY,SAAUC,GAMpB,QAASC,GAAWC,GAChBC,EAAQL,EAAEM,KAAMJ,GAChBG,EAAME,KAAMC,EAAIC,SACXC,KAAMF,EAAIG,QACVC,KAAMR,EAAMA,GATrB,GAAIC,GACAG,EAAMR,EAAEa,WACRC,EAAUN,EAAIM,SAkClB,OAvBAb,GAAUc,MAAOZ,GAGjBW,EAAQE,MAAQ,SAAUC,GAGtB,GAAKZ,EACD,MAAOA,GAAMW,MAAOC,EAIxB,IAAIF,GAAQd,EAAUc,QAClBG,EAAQlB,EAAEmB,QAAShB,EAAWY,EAQlC,OANKG,GAAQ,IACTH,EAAMK,OAAQF,EAAO,GAIzBV,EAAIa,WAAYnB,EAASoB,SAAWpB,GAAYY,EAASG,EAAY,KAC9DH,GAGJA,KAGRS"} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery.ajaxQueue", 3 | "title": "jQuery Ajax Queue", 4 | "description": "A simple queue for ajax requests", 5 | "version": "0.1.2pre", 6 | "homepage": "https://github.com/gnarf37/jquery-ajaxQueue", 7 | "author": { 8 | "name": "Corey Frang", 9 | "email": "gnarf37@gmail.com", 10 | "url": "https://github.com/gnarf37" 11 | }, 12 | "maintainers": [ 13 | { 14 | "name": "Corey Frang", 15 | "email": "gnarf37@gmail.com", 16 | "url": "https://github.com/gnarf37" 17 | } 18 | ], 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/gnarf37/jquery-ajaxQueue.git" 22 | }, 23 | "bugs": "https://github.com/gnarf37/jquery-ajaxQueue/issues", 24 | "licenses": [ 25 | { 26 | "type": "MIT", 27 | "url": "https://github.com/jquery/gnarf37/jquery-ajaxQueue/blob/master/LICENSE-MIT" 28 | } 29 | ], 30 | "dependencies": {}, 31 | "devDependencies": { 32 | "grunt-contrib-concat": "~0.1.1", 33 | "grunt-contrib-watch": "~0.5.3", 34 | "grunt-contrib-qunit": "~0.2.2", 35 | "grunt-contrib-jshint": "~0.6.4", 36 | "grunt-contrib-uglify": "~0.2.4", 37 | "grunt": "~0.4.1", 38 | "sinon": "~1.7.3", 39 | "jquery": "~1.8.3" 40 | }, 41 | "keywords": [ 42 | "ajax", 43 | "queue" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /src/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "boss": true, 11 | "eqnull": true, 12 | "browser": true, 13 | "predef": ["jQuery"] 14 | } 15 | -------------------------------------------------------------------------------- /src/jQuery.ajaxQueue.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | // jQuery on an empty object, we are going to use this as our Queue 4 | var ajaxQueue = $({}); 5 | 6 | $.ajaxQueue = function( ajaxOpts ) { 7 | var jqXHR, 8 | dfd = $.Deferred(), 9 | promise = dfd.promise(); 10 | 11 | // run the actual query 12 | function doRequest( next ) { 13 | jqXHR = $.ajax( ajaxOpts ); 14 | jqXHR.done( dfd.resolve ) 15 | .fail( dfd.reject ) 16 | .then( next, next ); 17 | } 18 | 19 | // queue our ajax request 20 | ajaxQueue.queue( doRequest ); 21 | 22 | // add the abort method 23 | promise.abort = function( statusText ) { 24 | 25 | // proxy abort to the jqXHR if it is active 26 | if ( jqXHR ) { 27 | return jqXHR.abort( statusText ); 28 | } 29 | 30 | // if there wasn't already a jqXHR we need to remove from queue 31 | var queue = ajaxQueue.queue(), 32 | index = $.inArray( doRequest, queue ); 33 | 34 | if ( index > -1 ) { 35 | queue.splice( index, 1 ); 36 | } 37 | 38 | // and then reject the deferred 39 | dfd.rejectWith( ajaxOpts.context || ajaxOpts, [ promise, statusText, "" ] ); 40 | return promise; 41 | }; 42 | 43 | return promise; 44 | }; 45 | 46 | })(jQuery); 47 | -------------------------------------------------------------------------------- /test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "boss": true, 11 | "eqnull": true, 12 | "browser": true, 13 | "predef": [ 14 | "jQuery", 15 | "QUnit", 16 | "module", 17 | "test", 18 | "asyncTest", 19 | "expect", 20 | "start", 21 | "stop", 22 | "ok", 23 | "equal", 24 | "notEqual", 25 | "deepEqual", 26 | "notDeepEqual", 27 | "strictEqual", 28 | "notStrictEqual", 29 | "raises" 30 | ] 31 | } -------------------------------------------------------------------------------- /test/jquery.ajaxQueue.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ajax Queue Test Suite 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Ajax Queue Test Suite

17 |

18 |
19 |

20 |
    21 |
    22 | lame test markup 23 | normal test markup 24 | awesome test markup 25 |
    26 | 27 | 28 | -------------------------------------------------------------------------------- /test/jquery.ajaxQueue_test.js: -------------------------------------------------------------------------------- 1 | /*global sinon, console*/ 2 | (function( $ ) { 3 | /* 4 | ======== A Handy Little QUnit Reference ======== 5 | http://docs.jquery.com/QUnit 6 | 7 | Test methods: 8 | expect(numAssertions) 9 | stop(increment) 10 | start(decrement) 11 | Test assertions: 12 | ok(value, [message]) 13 | equal(actual, expected, [message]) 14 | notEqual(actual, expected, [message]) 15 | deepEqual(actual, expected, [message]) 16 | notDeepEqual(actual, expected, [message]) 17 | strictEqual(actual, expected, [message]) 18 | notStrictEqual(actual, expected, [message]) 19 | raises(block, [expected], [message]) 20 | */ 21 | 22 | var requests; 23 | 24 | module( "ajaxQueue", { 25 | setup: function() { 26 | requests = []; 27 | this.xhr = sinon.useFakeXMLHttpRequest(); 28 | this.xhr.onCreate = function (xhr) { 29 | xhr.customRespond = function() { 30 | this.respond(200, { "Content-Type": "application/json" }, 31 | '{ "id": 10, "name": "ajaxQueue" }'); 32 | }; 33 | requests.push(xhr); 34 | }; 35 | }, 36 | teardown: function() { 37 | this.xhr.restore(); 38 | } 39 | }); 40 | 41 | asyncTest( "Smoke detection: Normal ajax request", 1, function () { 42 | $.ajax({ 43 | url: "mock.json", 44 | dataType: "json", 45 | success: function(data) { 46 | start(); 47 | equal(data.name, "ajaxQueue"); 48 | } 49 | }); 50 | requests[0].customRespond(); 51 | }); 52 | 53 | asyncTest( "Single ajaxQueue request", 1, function () { 54 | $.ajaxQueue({ 55 | url: "mock.json", 56 | dataType: "json", 57 | success: function(data) { 58 | start(); 59 | equal(data.name, "ajaxQueue"); 60 | } 61 | }); 62 | requests[0].customRespond(); 63 | }); 64 | 65 | asyncTest( "Concurrent ajaxQueue requests", 9, function () { 66 | $.ajaxQueue({ 67 | url: "first_mock.json", 68 | dataType: "json", 69 | success: function(data) { 70 | console.log("\n1st mock received"); 71 | equal(data.name, "ajaxQueue"); 72 | } 73 | }); 74 | $.ajaxQueue({ 75 | url: "second_mock.json", 76 | dataType: "json", 77 | success: function(data) { 78 | console.log("2nd mock received"); 79 | equal(data.name, "ajaxQueue"); 80 | } 81 | }); 82 | $.ajaxQueue({ 83 | url: "third_mock.json", 84 | dataType: "json", 85 | success: function(data) { 86 | console.log("3rd mock received"); 87 | start(); 88 | equal(data.name, "ajaxQueue"); 89 | } 90 | }); 91 | 92 | equal(requests.length, 1, "Only one request is called at a time"); 93 | equal(requests[0].url, "first_mock.json", "... and it's the 1st"); 94 | 95 | setTimeout(function() { 96 | requests[0].customRespond(); 97 | 98 | equal(requests.length, 2, "Next request is called after 1st is received"); 99 | equal(requests[1].url, "second_mock.json", "... and it's the 2nd"); 100 | 101 | setTimeout(function() { 102 | requests[1].customRespond(); 103 | 104 | equal(requests.length, 3, "Next request is called after 2nd is received"); 105 | equal(requests[2].url, "third_mock.json", "... and it's the 3rd"); 106 | 107 | requests[2].customRespond(); 108 | }, 500); 109 | }, 500); 110 | }); 111 | 112 | }(jQuery)); 113 | --------------------------------------------------------------------------------