搭配 async、await


ES8(ECMAScript 2017)新增了 asyncawait 語法,如〈Promise〉中談過的,可以用來令非同步的流程,不用是透過回呼函式,而像是循序撰寫的語法。

Fetch API 有許多方法,都是傳回 Promise,因此搭配 asyncawait 語法,可以令程式更為簡潔,例如〈簡介 Fetch API〉中的第一個範例,可以改寫為:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

    <button id='req'>取得表格</button>
    <div id="table"></div>

<script type="text/javascript">

    document.getElementById('req').onclick = async function() {
        let resp = await fetch('XMLHttpRequest-1.txt');
        let text = await resp.text();
        document.getElementById('table').innerHTML = text;
    };

</script>   

</body>
</html>

按我觀看執行結果

await 可以承接傳回 Promise 的 API,直到有結果之後,流程才會往下一步,await 只能撰寫在 async 標示的函式之中,然而,事件處理器可以標示為 async,這使得程式撰寫起來簡單許多。

來將〈簡介 Fetch API〉中第二個範例改寫為 asyncawait

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

    新增書籤:<br>
    網址:<input id="url" type="text">
    <span id="message" style="color:red"></span><br>
    名稱:<input type="text">

<script type="text/javascript">    
    function params(paraObj) {
        return Object.keys(paraObj)
                     .map(name => {
                         let paraName = encodeURIComponent(name);
                         let paraValue = encodeURIComponent(paraObj[name]);                         
                         return `${paraName}=${paraValue}`.replace(/%20/g, '+');
                     })
                     .join('&');
    }

    document.getElementById('url').onblur = async function() {
        let reqString = params({ 
            url : document.getElementById('url').value 
        });

        let resp = await fetch('POST-1.php', {
            method : 'POST',
            headers : {
                'Content-Type' : 'application/x-www-form-urlencoded'
            },
            body : reqString
        });

        let text = await resp.text();
        if(text === 'existed') {
            document.getElementById('message').innerHTML = 'URL 已存在';
        }    

    };

</script>

</body>
</html>

按我觀看執行結果

底下則是將〈簡介 Fetch API〉中最後一個範例改寫為 asyncawait

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

    <body>
        ID:<input id="id">
        <button id="test">JSONP 測試</button>
        <span id="result"></span>
    </body>

<script type="text/javascript">

    document.getElementById('test').onclick = async function() {
        let id = document.getElementById('id').value;
        let result = document.getElementById('result');

        let resp = await fetch(`https://openhome.cc/Gossip/ECMAScript/samples/CORS-1.php?id=${id}`, {
            mode : 'cors'
        });
        let person = await resp.json();
        result.innerHTML = `${person.name}, ${person.age}`;
    };

</script>    

</body>
</html>

按我觀看執行結果

當然,Promise 是一種流暢風格,而 asyncawait 是另一種風格,就看各自偏好哪個了。