Skip to content

Instantly share code, notes, and snippets.

@sketchpunk
Last active February 24, 2020 16:58
Show Gist options
  • Save sketchpunk/0240ddf7dd5c81f894ec3a65e90a9447 to your computer and use it in GitHub Desktop.
Save sketchpunk/0240ddf7dd5c81f894ec3a65e90a9447 to your computer and use it in GitHub Desktop.
XhrPromise = XMLHttpRequest + Promise
/*
let xhr = new XhrPromise();
xhr.get(url, "json").then( r=>console.log(r) ).catch(err=>console.log(err));
*/
class XhrPromise{
static get( url, type ){
let xhr = new XhrPromise();
return xhr.get.apply( xhr, arguments );
}
constructor(){
this.xhr = new XMLHttpRequest();
this.xhr.addEventListener("load", this.onXhrComplete.bind( this ) ,false);
this.xhr.addEventListener("error", this.onXhrError.bind( this ) ,false);
this.xhr.addEventListener("abort", this.onXhrAbort.bind( this ) ,false);
this.xhr.addEventListener("timeout", this.onXhrTimeout.bind( this ) ,false);
this.total_items = 0;
this.queue = null;
this.complete = null;
this.progress_cb = null;
this.url = null;
this.resType = null;
//Start request in promise and save Resolve/Reject reference to call later
//when the async xhr call is complete.
this._onPromise = ( resolve, reject )=>{
this.promiseResolve = resolve;
this.promiseReject = reject;
if( this.queue != null ) this._queueNext();
else this._get();
};
}
on_progress( cb ){
this.progress_cb = cb;
this.xhr.addEventListener("progress", this.onXhrProgress.bind( this ) ,false);
return this;
}
get( url, type ){
if( arguments.length > 2 ){
this.queue = new Array();
this.complete = new Array();
for(let i=0; i < arguments.length; i+=2 ){
this.queue.push({ url:arguments[i], type:arguments[i+1] });
}
this.total_items = this.queue.length;
}else{
this.url = url;
this.resType = type;
}
return new Promise( this._onPromise );
}
_queueNext(){
if( this.queue.length == 0 ) return false;
let itm = this.queue.shift();
this.url = itm.url;
this.resType = itm.type;
this._get();
return true;
}
_get(){
//console.log("GET", this.url, this.resType );
this.xhr.open( "GET", this.url, true );
this.xhr.responseType = this.resType || "text";
try{
this.xhr.send();
}catch(err){
console.error("xhr err",err);
this._complete( false, err );
}
}
_complete( isSuccess, err ){
//console.log( "Complete", isSuccess, err );
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if( !isSuccess ){
this.promiseReject( err );
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}else{
// Handling a queue of items.
if( this.queue ){
this.complete.push( this.xhr.response );
if( this._queueNext() ) return;
this.promiseResolve( this.complete );
}else{
this.promiseResolve( this.xhr.response );
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this.queue = null;
this.complete = null;
this.url = null;
this.resType = null;
}
onXhrComplete( e ){ //console.log( e, e.currentTarget.response );
if(this.xhr.status != 200) this._complete( false, "http status : " + this.xhr.status + " " + this.xhr.statusText );
else this._complete( true );
}
onXhrError( e ){ this._complete( false ); console.error("onXhrError"); }
onXhrAbort( e ){ this._complete( false ); console.error("onXhrAbort"); }
onXhrTimeout( e ){ this._complete( false ); console.error("onXhrTimeout"); }
onXhrProgress( e ){
let itms = (this.queue)? this.queue.length+1 : 0,
prog = e.loaded,
total = e.total,
per = prog / total;
console.log( this.total_items - itms, this.total_items, prog, total, per);
}
}
export default XhrPromise;
class XhrPromise{
xhr = new XMLHttpRequest();
mode = "";
url = null;
body = null;
body_type = null;
res_type = "text";
resolve = null;
reject = null;
active = false;
constructor(){
this.xhr.self = this;
this.xhr.addEventListener( "load", XhrPromise.on_complete, false);
this.xhr.addEventListener( "error", XhrPromise.on_error , false);
this.xhr.addEventListener( "abort", XhrPromise.on_abort, false);
this.xhr.addEventListener( "timeout", XhrPromise.on_timeout, false);
}
get( url, res_type ){
if( this.active ) return null;
this.url = url;
this.res_type = res_type || "text";
this.mode = "GET";
this.body = null;
return new Promise( this._promise.bind( this ) );
}
post( url, body, res_type, body_type ){
if( this.active ) return null;
this.url = url;
this.res_type = res_type || "text";
this.mode = "POST";
if( body ){
if( body instanceof FormData ){
this.body = body;
this.body_type = null; //"form" FromData changes the Content-Type to whatever it needs to use. So no point assigning it.
}else if( Array.isArray( body ) || typeof body === "object" ){
this.body = JSON.stringify( body );
this.body_type = "json";
}else{
this.body = body;
}
}
if( body_type ) this.body_type = body_type;
return new Promise( this._promise.bind( this ) );
}
_promise( resolve, reject ){
this.resolve = resolve;
this.reject = reject;
this.active = true;
this.xhr.open( this.mode, this.url, true );
this.xhr.responseType = this.res_type;
/* console.log( this.body_type, this.body ); */
switch( this.body_type ){
case "json": this.xhr.setRequestHeader( "Content-Type", "application/json;charset=UTF-8" ); break;
case "form": this.xhr.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" ); break;
case "multi": this.xhr.setRequestHeader( "Content-Type", "multipart/form-data" ); break;
}
try{ this.xhr.send( this.body ); }
catch( err ){ this._complete( false, err ); }
};
_complete( isSuccess, err ){
//console.log( "xhr_complete", isSuccess, err );
if( !isSuccess ) this.reject( err );
else{
// IE11 doesn't support response types,
// So if requesting a JSON response, Parse it before returning it.
let rtn;
if( this.res_type == "json" && typeof this.xhr.response == "string" ){
rtn = JSON.parse( this.xhr.response );
}else{
rtn = this.xhr.response;
}
this.resolve( rtn );
}
this.dispose();
}
dispose(){
this.resolve = null;
this.reject = null;
this.xhr.self = null;
this.xhr.removeEventListener( "load", XhrPromise.on_complete, false );
this.xhr.removeEventListener( "error", XhrPromise.on_error , false );
this.xhr.removeEventListener( "abort", XhrPromise.on_abort, false );
this.xhr.removeEventListener( "timeout", XhrPromise.on_timeout, false );
}
static on_error = function( e ){ this.self._complete( false ); console.error("onXhrError"); }
static on_abort = function( e ){ this.self._complete( false ); console.error("onXhrAbort"); }
static on_timeout = function( e ){ this.self._complete( false ); console.error("onXhrTimeout"); }
static on_complete = function( e ){ //console.log( e, e.currentTarget.response );
let xhr = e.srcElement;
if( xhr.status != 200 ) xhr.self._complete( false, "http status : " + xhr.status + " " + xhr.statusText );
else xhr.self._complete( true );
}
static get( url, type ){ return new XhrPromise().get( url, type ); }
static post( url, body, res_type, body_type ){ return new XhrPromise().post( url, body, res_type, body_type ); }
}
window.XhrPromise = XhrPromise;
export default XhrPromise;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment