使用promise手動封裝ajax函數


最近在做一個單頁應用,node和瀏覽器僅通過json傳輸數據,因為是只有自己用等於是鍛煉一下自己,所以也不用考慮seo的問題,node端我已經寫好了,但是瀏覽器端想要用ajax原生太麻煩,用封裝的函數又需要引入angular,jquery等大型框架。我寫node比較多,覺得用什么功能就引入什么功能,不太喜歡用大而全的框架,所以只好手動封裝一下ajax的操作

 

ajax的xhr對象有7個事件

onloadstart                    開始send觸發

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

onload                           得到響應

onerror                          服務器異常

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)) })

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM