Last active
November 27, 2015 02:37
-
-
Save chrisguitarguy/b1a5681b2e29e6619323 to your computer and use it in GitHub Desktop.
Turns out it's pretty simple to write a little promised-based ajax wrapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var assign = require('object-assign'); | |
var Promise = require('es6-promise').Promise; | |
var EventEmitter = require('eventemitter2').EventEmitter2; | |
function buildQueryString(object, prefix) { | |
var out = []; | |
for (var prop in object) { | |
if (!object.hasOwnProperty(prop)) { | |
continue; | |
} | |
var k = prefix ? prefix+'['+prop+']' : prop; | |
var v = object[prop]; | |
out.push(typeof v in 'object' ? | |
buildQueryString(v, k) : | |
encodeURIComponent(k)+'='+encodeURIComponent(v)); | |
} | |
return out.join('&'); | |
} | |
function appendQueryString(url, qsAsObject) { | |
if (!qsAsObject || typeof qsAsObject !== 'object') { | |
return url; | |
} | |
url += '?'+buildQueryString(qsAsObject); | |
return url; | |
} | |
function makeResponse(xhr) { | |
return Object.create(Object.prototype, { | |
status: { | |
configurable: false, | |
enumerable: true, | |
writeable: false, | |
value: xhr.status | |
}, | |
statusText: { | |
configurable: false, | |
enumerable: true, | |
writeable: false, | |
value: xhr.statusText | |
}, | |
type: { | |
configurable: false, | |
enumerable: true, | |
writeable: false, | |
value: xhr.responseType | |
}, | |
body: { | |
configurable: false, | |
enumerable: true, | |
writeable: false, | |
value: xhr.response | |
} | |
}); | |
} | |
function ServerError(resp, xhr) { | |
Error.apply(this); | |
this.message = resp.statusText; | |
this.response = resp; | |
this.xhr = xhr; | |
this.name = 'ajax.ServerError'; | |
} | |
ServerError.prototype = Object.create(Error.prototype); | |
function NetworkError(xhr) { | |
Error.apply(this); | |
this.name = 'ajax.NetworkError'; | |
this.message = 'Network Error'; | |
this.xhr = xhr; | |
} | |
NetworkError.prototype = Object.create(Error.prototype); | |
function readyStateChangeHandler(xhr, resolve, reject) { | |
if (xhr.readyState !== XMLHttpRequest.DONE) { | |
return; | |
} | |
var response = makeResponse(xhr); | |
this.emit('request:received', xhr, response) | |
if (xhr.status >= 200 && xhr.status < 300) { | |
this.emit('request:succeeded', xhr, response); | |
resolve(response); | |
} else { | |
var err = new ServerError(response, xhr); | |
this.emit('request:failed', xhr, err); | |
reject(err); | |
} | |
} | |
function errorHandler(xhr, reject) { | |
var err = new NetworkError(xhr); | |
this.emit('request:errored', xhr, err); | |
reject(err); | |
} | |
function Ajax(config) { | |
EventEmitter.call(this, { | |
wildcard: true, | |
delimiter: ':' | |
}); | |
config || (config = {}); | |
this._beforeSend = config.beforeSend || function (xhr) { }; | |
}; | |
function _methodRequest(method) { | |
return function (url, data, config) { | |
config || (config = {}); | |
config.data = data; | |
return this.request(method, url, config); | |
} | |
} | |
Ajax.prototype = assign({}, EventEmitter.prototype, { | |
request: function (method, url, config) { | |
console.log(this); | |
config || (config = {}); | |
method = method.toUpperCase(); | |
var body = undefined; | |
switch (method) { | |
case 'GET': | |
case 'HEAD': | |
url = appendQueryString(url, config.data); | |
break; | |
case 'POST': | |
case 'PUT': | |
case 'PATCH': | |
case 'DELETE': | |
body = config.data; | |
break; | |
default: | |
throw new Error(method+' requests are not supported') | |
} | |
var xhr = this._createRequest(method, url, config); | |
return this._createPromise(xhr, body); | |
}, | |
get: _methodRequest('GET'), | |
head: _methodRequest('HEAD'), | |
post: _methodRequest('POST'), | |
put: _methodRequest('PUT'), | |
patch: _methodRequest('PATCH'), | |
// delete | |
destroy: _methodRequest('DELETE'), | |
_createRequest: function (method, url, config) { | |
var xhr = new XMLHttpRequest(); | |
var headers = config.headers || {}; | |
xhr.open(method, url); | |
xhr.responseType = "json"; | |
(config.beforeSend || this._beforeSend).call(this, xhr, config); | |
for (var h in headers) { | |
xhr.setRequestHeader(h, headers[h]); | |
} | |
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); | |
xhr.setRequestHeader('Accept', 'application/json'); | |
return xhr; | |
}, | |
_createPromise: function (xhr, body) { | |
return new Promise(function (resolve, reject) { | |
xhr.onreadystatechange = readyStateChangeHandler.bind(this, xhr, resolve, reject); | |
xhr.onerror = errorHandler.bind(this, xhr, reject); | |
this.emit('request:sending', xhr, body); | |
xhr.send(body); | |
this.emit('request:sent', xhr, body); | |
}.bind(this)); | |
} | |
}); | |
module.exports = { | |
Ajax: Ajax, | |
ServerError: ServerError, | |
NetworkError: NetworkError | |
}; | |
// make the api accesible via a "default" object | |
var defaultAjax = new Ajax(); | |
var methods = [ | |
'request', | |
'get', | |
'head', | |
'post', | |
'put', | |
'patch', | |
'destroy', | |
'once', | |
'many', | |
'on', | |
'onAny', | |
'off', | |
'offAny', | |
'removeAllListeners', | |
'listeners', | |
'listenersAny' | |
]; | |
for (var i in methods) { | |
var m = methods[i]; | |
module.exports[m] = defaultAjax[m].bind(defaultAjax); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "chrisguitarguy-ajax", | |
"description": "Ajax library to play around with promises, etc", | |
"dependencies": { | |
"object-assign": "~4.0", | |
"es6-promise": "~3.0", | |
"eventemitter2": "~0.4" | |
}, | |
"devDependencies": { | |
"browserify": "~12.0", | |
"babelify": "~7.2", | |
"babel-preset-es2015": "~6.1", | |
"vinyl-source-stream": "~1.1", | |
"gulp": "~3.9" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment