最近更新: 2021-08-21

學習 ECMAScript/JavaScript 6 - Promise 實際範例

我從過去用到 Promise 的 Web 前端開發案中,挑出兩個具有泛用性的應用函數,作為實際範例。若是不了解 Promise 基礎觀念,請先看「ECMAScript/JavaScript 6 - Promise 學習六步 」。

Promise 的應用函數:

  1. JSONRequest
  2. Until

JSONRequest

用 Promise 包裝的 XMLHttpRequest 函數。功能明確,呼叫 RESTful API 取回 JSON 資料。

下列為函數內容。也保存在我的 github: ajax.js


/**
使用 Promise 實作,Promise 是 ECMAScript 6 規範項目。
因此需要支持 ECMAScript 6 標準的瀏覽器, IE 所有版本都不可用。
 */
function JSONRequest(http_method, url, body) 
{
    let promise = new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();

        xhr.addEventListener('load', () => {
            // console.log('response loaded', xhr);
            if (Number(xhr.status) >= 400) {
                reject(xhr);
            }
            else if (xhr.responseText.length < 1) {
                // console.log('only status code');
                resolve();
            }
            else {
                let result = null;
                try {
                    result = JSON.parse(xhr.responseText);
                }
                catch (e) {
                    reject(xhr);
                }
                resolve(result);
            }
        });

        xhr.addEventListener('error', () => {
            console.log("request failed");
            reject(xhr);
        });

        xhr.open(http_method, url);
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.setRequestHeader('Content-Type', 'application/json');
        if (body && typeof(body) == 'object') {
            body = JSON.stringify(body);
        }
        xhr.send(body);
    });

    return promise;
}

使用情境如專案中使用 jQuery slim (不含 ajax 模組) 之時, JSONRequest 就是 jQuery.ajax 的替代品。

用法如下:


JSONRequest('GET', url)
.then(result => {
    console.log('get response ', result);
})

let data = {
    "name": "rock",
    "level": 123
};

JSONRequest('POST', url, data)
.then(result => {
    // result 已經分析成 JavaScript object
    console.log('post response ', result);
})

Until

用 Promise 包裝的 setInterval 函數。

下列為函數內容。也保存在我的 github: until.js

引數 expr 是一個函數,它應包含設計者指示的條件判斷內容,並回傳 true 或 false 。 Until() 會以 0.1 秒的頻率呼叫 expr 。當 expr 條件成立時,才執行 then 的內容。

引數 timeout 指定 Until 的最長工作秒數。若 expr 在超過 timeout 秒數後仍然為 false ,則執行 catch 的內容。 timeout 可省略,表示不限時。


function Until(expr, timeout)
{
    let _timeout = timeout ? timeout * 10 : undefined;

    let promise = new Promise((resolve, reject) => {
        let count = 0;
        let it = setInterval(() => {
            if (expr()) {
                clearInterval(it);
                resolve(count/10); // elapsed seconds
                return;
            }

            ++count;
            if (_timeout && count > _timeout) { // wait n seconds
                clearInterval(it);
                reject();
                return;
            }
        }, 100) // 0.1 second interval.
    });

    return promise;
}

使用情境,主要用在限時操作的場合。當然也可以不限時,但這種情境較少見。

例如我要監測頁面某個輸入控制項的內容是否有值。而輸入來源可能是多個 Ajax 請求的其中一個。 而且這些 Ajax 請求可能會逾時,我不想讓使用者空等,所以限制這個輸入控制項應該在幾秒內有值。

範例的 expr 引數是 check_data() ,它會查看 id 為 DataInput 的 input 控制項是否有值。 timeout 限時 10 秒。


function check_data()
{
    return (document.getElementById('DataInput').value != '');
}

Until(check_data, 10)
.then(_=>{
    console.log('data completed.');
})
.catch(_=>{
    alert('查詢逾時,請稍候再試');
})