async await 續集: await 到底可以接什么?僅僅是 Promise嗎?


眾所周知,async await 只是 Promise 的語法糖,但具體是什么語法糖,我自己之前也沒細究。

昨天在研究 iOS JavaScriptCore 里邊如何捕獲未處理的 Promise rejection,發現 jscore 本身並不提供任何接口,只能想其他辦法繞過去。

參考了 Egret Native 的實現,發現他們實現和自己的臆想也是吻合的,就是在 JS 側對 Promise 做覆蓋,或者叫 polyfill,這樣就能完整的掌控 Promise 實現和 reject 的處理了。當然,這樣做是有缺陷的,只能捕獲 Promise,但 async await 方法的報錯就無法捕獲了,除非 JS 側把這些都轉義為 ES5。

本文就是簡單探討一下 await 后邊可以跟什么內容,這個和我的目標——“捕獲各種 Promise reject”是有關聯的。

 

1 await 接 Promise 實例

這個是最基礎用法,等待 Promise resolve 或 reject。resolve 后就同步執行,reject 就被 try catch 捕獲,或者不處理,由上層調用方法處理。

有個比較有趣的點是,無論是 js 側 polyfill 實現的 Promise,還是瀏覽器原生的 Promise,都可以接在 await 后,為什么呢?

    var b = new Promise((r,reject)=>reject('Promise reject result'));
    var basync = (async function(){
        try{
            await b;  
        }catch(e){
            console.log('error',e);
        }
    })();

  

2 await 接普通變量

這個是不推薦用法,但瀏覽器不會報錯,等同於 await 是多余的。當然,我們自己不會直接寫出這樣的代碼,往往是下游方法,可能某些分支情況下,直接返回了結果,而不是 Promise。正好瀏覽器這樣的兼容處理,就有利於 await 后接一個有動態返回類型的 Function。

    var c = {name: 'kenko'};
    var casync = (async function(){
        try{
            await c;    // await 不起作用,等於直接同步執行
            console.log('ccc');
        }catch(e){
            console.log('error',e);
        }
    })();

  

3 await 接 Thenable 對象 

這個才是真正答案。什么是 Thenable,參考 MDN 對 await 的定義:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

 

Thenable 其實就是帶有 then 方法的對象,這個 then 方法應該接受兩個參數,一個是 resolve 回調,一個是 reject 回調,類似 Promise 的 then 方法。

所以,當然,Promise 是一種 Thenable 實現,無論瀏覽器原生的 Promise 還是 polyfill 的 Promise 都符合 Thenable 規范,所以剛才第一種情況下的疑問也解開了。await 后接 Promise 是最常見情況。

 

那么 await 這個語法糖,實際具體做的事就有幾點:

1. 調用接的對象的 then 方法,分別傳入 resolve 和 reject 作為回調。

2. 如果 thenable 對象 resolve 了,那么 await 把 resolve 結果賦值給前邊變量(如果有),然后同步執行下一行代碼;

3. 如果 thenable 對象 reject 了,那么 await 把 reject 內容,throw 出去,所以緊接着如果有 try catch,這個 throw 內容就會被捕獲。如果沒有 try catch,這個 throw 會被整個 async 方法捕獲,作為對上層的 reject。

 

最后貼一個對比三種情況的 Demo:(polyfill 的 Promise,一個簡單的 Thenable,一個普通對象)

https://github.com/kenkozheng/HTML5_research/blob/master/Promise/await.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Await</title>
</head>
<body>
<script>window.Promise = undefined;</script>
<script src="https://cdn.bootcss.com/es6-promise/4.1.1/es6-promise.auto.js"></script>
<script>
    function MyPromise(resolver){
        resolver((r)=>{
            this._result = r;
            this._state = 1;
        }, (r)=>{
            this._result = r;
            this._state = 2;
        });
    }

    var prototype = MyPromise.prototype;
    prototype._state = 0;
    prototype._result = undefined;
    prototype._resolveCallbackList = [];
    prototype._rejectCallbackList = [];
    prototype.then = function(onResolved, onRejected){
        onResolved && this._resolveCallbackList.push(onResolved);
        this._rejectCallbackList.push(onRejected);
        this.trigger();
        return this;
    };
    prototype.catch = function(onRejected){
        this._rejectCallbackList.push(onRejected);
        this.trigger();
        return this;
    };
    prototype.trigger = function(){
        if(this._state === 1) {
            this._resolveCallbackList.forEach(func => func(this._result));
            this._resolveCallbackList = [];
        } else if(this._state === 2) {
            this._rejectCallbackList.forEach(func => func(this._result));
            this._rejectCallbackList = [];
        }
    }

    var aRejected = new MyPromise((resolve,reject)=>resolve('MyPromise reject result'));
    var aRejectedAsync = (async function(){
        try{
            await aRejected; //自定義的非嚴格A+ Promise實現,但是符合條件的thenable對象,await會等待
            console.log('a resolved');
        }catch(e){
            console.log('error', e);
        }
    })();

    var aResolved = new MyPromise((r,reject)=>reject('MyPromise reject result'));
    var aResolvedAsync = (async function(){
        try{
            await aResolved; //自定義的非嚴格A+ Promise實現,但是符合條件的thenable對象,await會等待
        }catch(e){
            console.log('error', e);
        }
    })();

    var b = new Promise((r,reject)=>reject('Promise reject result'));
    var basync = (async function(){
        try{
            await b;  // 使用的是polyfill版本Promise,實際就是一個function class實例
        }catch(e){
            console.log('error',e);
        }
    })();

    var c = {name: 'kenko'};
    var casync = (async function(){
        try{
            await c;    // await 不起作用,等於直接同步執行
            console.log('ccc');
        }catch(e){
            console.log('error',e);
        }
    })();
</script>
</body>
</html>

  


免責聲明!

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



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