從小小題目逐步走進 JavaScript 異步調用

問題

原題來自 @若澤提問javascript

可修改下面的 aa() 函數,目的是在一秒後用 console.log() 輸出 want-valuejava

function aa() {
    setTimeout(function() {
        return "want-value";
    }, 1000);
}

可是,有額外要求:面試

  1. aa() 函數能夠隨意修改,可是不能有 console.log()
  2. 執行 console.log() 語句裏不能有 setTimeout 包裹

解答

也許這是個面試題,管它呢。問題的主要目的是考察對異步調用執行結果的處理,既然是異步調用,那麼不可能同步等待異步結果,結果必定是異步的segmentfault

setTimeout() 常常用來模擬異步操做。最先,異步是經過回調來通知(調用)處理程序處理結果的異步

function aa(callback) {
    setTimeout(function() {
        if (typeof callback === "function") {
            callback("want-value");
        }
    }, 1000);
}

aa(function(v) {
    console.log(v);
});

不過回調在用於稍大型一點的異步應用時,容易出現多層嵌套,因此以後提出了一些對其進行「扁平」化,這一部分能夠參考閒談異步調用「扁平」化。固然 Promise 是很是流行的一種方法,並最終被 ES6 採納。用 Promise 實現以下:async

function aa() {
    return new Promise(resolve => {
        setTimeout(function() {
            resolve("want-value");
        }, 1000);
    });
}

aa().then(v => console.log(v));

就這個例子來講,它和前面回調的例子大同小異。不過它會引出目前更推薦的一種方法——async/await,從 ES2017 開始支持:函數

function aa() {
    return new Promise(resolve => {
        setTimeout(function() {
            resolve("want-value");
        }, 1000);
    });
}

async function main() {
    const v = await aa();
    console.log(v);
}

main();

aa() 的定義與 Promise 方法中的定義是同樣的,可是在調用的時候,使用了 await,異步等待,等待到異步的結果以後,再使用 console.log() 對其進行處理。code

這裏須要注意的是 await 只能在 async 方法中使用,因此爲了使用 await 必須定義一個 async 的 main 方法,並在全局做用域中調用。因爲 main 方法是異步的(申明爲 async),因此若是 main() 調用以後還有其它語句,好比 console.log("hello"),那麼這一句話會先執行。ip

async/await 語法讓異步調用寫起來像寫同步代碼,在編寫代碼的時候,能夠避免邏輯跳躍,寫起來會更輕鬆。(參考:從地獄到天堂,Node 回調向 async/await 轉變作用域

固然,定義 main() 再調用 main() 這部分能夠用 IIFE 封裝一下,

(async () => {
    const v = await aa();
    console.log(v);
})();
相關文章
相關標籤/搜索