從小小題目逐步走進 JavaScript 異步調用
原題來自 @若澤[wangwenlin] 的提問。
可修改下面的 aa()
函數,目的是在一秒後用 console.log()
輸出 want-value
function aa() {
setTimeout(function() {
return "want-value";
}, 1000);
}
但是,有額外要求:
aa()
函數可以隨意修改,但是不能有console.log()
- 執行
console.log()
語句裏不能有setTimeout
包裹
解答
也許這是個面試題,管它呢。問題的主要目的是考察對異步調用執行結果的處理,既然是異步調用,那麽不可能同步等待異步結果,結果一定是異步的
setTimeout()
經常用來模擬異步操作。最早,異步是通過回調來通知(調用)處理程序處理結果的
function aa(callback) {
setTimeout(function() {
if (typeof callback === "function") {
callback("want-value");
}
}, 1000);
}
aa(function(v) {
console.log(v);
});
不過回調在用於稍大型一點的異步應用時,容易出現多層嵌套,所以之後提出了一些對其進行“扁平”化,這一部分可以參考閑談異步調用“扁平”化。當然 Promise 是非常流行的一種方法,並最終被 ES6 采納。用 Promise 實現如下:
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()
對其進行處理。
這裏需要註意的是 await
只能在 async
方法中使用,所以為了使用 await
必須定義一個 async
的 main 方法,並在全局作用域中調用。由於 main 方法是異步的(申明為 async),所以如果 main()
調用之後還有其它語句,比如 console.log("hello")
,那麽這一句話會先執行。
async/await 語法讓異步調用寫起來像寫同步代碼,在編寫代碼的時候,可以避免邏輯跳躍,寫起來會更輕松。(參考:從地獄到天堂,Node 回調向 async/await 轉變)
當然,定義 main()
再調用 main()
這部分可以用 IIFE 封裝一下,
(async () => {
const v = await aa();
console.log(v);
})();
從小小題目逐步走進 JavaScript 異步調用