使用promise手動封裝ajax函數

最近在作一個單頁應用,node和瀏覽器僅經過json傳輸數據,由於是隻有本身用等因而鍛鍊一下本身,因此也不用考慮seo的問題,node端我已經寫好了,可是瀏覽器端想要用ajax原生太麻煩,用封裝的函數又須要引入angular,jquery等大型框架。我寫node比較多,以爲用什麼功能就引入什麼功能,不太喜歡用大而全的框架,因此只好手動封裝一下ajax的操做node

 

ajax的xhr對象有7個事件jquery

onloadstart                    開始send觸發ajax

onprogress                    從服務器上下載數據每50ms觸發一次json

onload                           獲得響應segmentfault

onerror                          服務器異常promise

onloadend                      請求結束,不管成功失敗瀏覽器

onreadystatechange        xhr.readyState改變使觸發服務器

onabort                         調用xhr.abort時觸發框架

 

關於ajax,我找到了一篇很好的文章,這裏就再也不贅述:https://segmentfault.com/a/1190000004322487異步

 

原本我是想註冊onload和onerror簡單封裝一下,後來發現即便後臺返回500也依舊會觸發onload事件,

因此最終個人選擇是註冊onloadend事件,經過判斷xhr.status來決定是引起resolve仍是reject,同時

對於xhr對象還註冊ontimeout,onabort,onerror函數,並在reject時返回一個對象,包含errorType和xhr,

返回xhr的緣由在於使用這個函數時可能使用xhr對象的一些其餘參數,resolve也是返回這個

 

代碼以下:

// ajax函數的默認參數
var ajaxOptions = {
    url: '#',
    method: 'GET',
    async: true,
    timeout: 0,
    data: null,
    dataType: 'text',
    headers: {},
    onprogress: function () { },
    onuploadprogress: function () { },
    xhr: null
}

/**
 * ajax函數,返回一個promise對象
 * @param {Object} optionsOverride 參數設置,支持的參數以下
 *   url:                     url地址,默認"#"
 *   method:                  請求方法,僅支持GET,POST,默認GET
 *   async:                   是否異步,默認true
 *   timeout:                 請求時限,超時將在promise中調用reject函數
 *   data:                    發送的數據,該函數不支持處理數據,將會直接發送
 *   dataType:                接受的數據的類型,默認爲text
 *   headers:                 一個對象,包含請求頭信息
 *   onprogress:              處理onprogress的函數
 *   ouploadprogress:         處理.upload.onprogress的函數
 *   xhr:                     容許在函數外部建立xhr對象傳入,但必須不能是使用過的
 * @return {Promise} 
 *   該函數註冊xhr.onloadend回調函數,判斷xhr.status是否屬於 [200,300)&&304 ,
 *   若是屬於則promise引起resolve狀態,容許拿到xhr對象
 *   若是不屬於,或已經引起了ontimeout,onabort,則引起reject狀態,容許拿到xhr對象
 * 
 * 關於reject
 *   返回一個對象,包含
 *   errorType:錯誤類型,
 *     abort_error:   xhr對象調用abort函數
 *     timeout_error: 請求超時
 *     onerror:       xhr對象觸發了onerror事件
 *     send_error:    發送請求出現錯誤
 *     status_error:  響應狀態不屬於 [200,300)&&304
 */
function ajax(optionsOverride) {
    // 將傳入的參數與默認設置合併
    var options = {};
    for (var k in ajaxOptions) {
        options[k] = optionsOverride[k] || ajaxOptions[k];
    }
    options.async = options.async === false ? false : true;
    var xhr = options.xhr = options.xhr || new XMLHttpRequest();

    return new Promise(function (resolve, reject) {
        xhr.open(options.method, options.url, options.async);
        xhr.timeout = options.timeout;

        //設置請求頭
        for (var k in options.headers) {
            xhr.setRuquestHeader(k, options.headers[k]);
        }

        // 註冊xhr對象事件
        xhr.onprogress = options.onprogress;
        xhr.upload.onprogress = options.onuploadprogress;
        xhr.responseType = options.dataType;

        xhr.onabort = function () {
            reject(new Error({
                errorType: 'abort_error',
                xhr: xhr
            }));
        }
        xhr.ontimeout = function () {
            reject({
                errorType: 'timeout_error',
                xhr: xhr
            });
        }
        xhr.onerror = function () {
            reject({
                errorType: 'onerror',
                xhr: xhr
            })
        }
        xhr.onloadend = function () {
            if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)
                resolve(xhr);
            else
                reject({
                    errorType: 'status_error',
                    xhr: xhr
                })
        }

        try {
            xhr.send(options.data);
        }
        catch (e) {
            reject({
                errorType: 'send_error',
                error: e
            });
        }
    })
}

 

另一點,關於參數我沒有過多的檢查,好比method是否爲'GET'或'POST',一個是在於參數檢查很繁瑣(盡情說我懶把),另一個在於參數應該使用強制規範,在文檔中標註,使用這個函數出錯了你就應該本身負責,而不是我幫你檢查後給你修改過來,這也不利於調用者理解這個函數。

 

使用代碼以下:

ajax({
    url: 'http://localhost:3000/suc',
    async: true,
    onprogress: function (evt) {
        console.log(evt.position/evt.total);
    },
    dataType:'text/json'
})
    .then(function (xhr) { console.log(xhr.response.name); },
    function (e) { console.log(JSON.stringify(e)) })
相關文章
相關標籤/搜索