什麼是異步編程,異步編程簡單來講就是:執行一個指令不會立刻返回結果而執行下一個任務,而是等到特定的事件觸發後,才能獲得結果。javascript
異步編程時就須要指定異步任務完成後須要執行的指令,總的來講有如下幾種「指定異步指令」的方式:java
早期的javascript的異步的實現也相似於這種類的屬性的方式:每一個類實例的相關回調事件有相應的handler(onclick,onchange,onload) 等。node
在DOM0級事件處理程序,就是將一個函數賦值給一個元素的屬性。git
element.onclick=function(){
console.log("clicked");
}
window.onload=function(){
console.log("loaded");
}
複製代碼
因爲javascript支持函數式編程,JavaSCript語言對異步編程的實現能夠用回調函數。 DOM2級事件解決了這個問題以上兩個問題github
element.addEventListener("click",function(){
console.log("clicked");
});
複製代碼
Promise很好的解決了"並行"的問題,咱們看看用promise庫怎麼發送get請求:編程
import fetch from 'node-fetch';
fetch('https://api.github.com')
.then((res)=>res.json())
.then((json)=>console.log("json:",json))
複製代碼
能夠看到promise把原來嵌套的回調,改成級連的方式了,實際是一種代理(proxy)。json
新建一個promise實例:api
let promise = new Promise((resolve, reject)=>{
// 異步操做的代碼
if(/* 異步操做成功 */){
resolve(value);
} else {
reject(error);
}
});
複製代碼
promise把成功和失敗分別代理到resolved 和 rejected . 同時還能夠級連catch異常。數組
簡單來講generator能夠理解爲一個可遍歷的狀態機。promise
語法上generator,有兩個特徵:
因爲generator是一個狀態機,因此須要手動調用 next才能執行,但TJ大神開發了co模塊,能夠自動執行generator。
import co from 'co';
co(function* (){
let now = Date.now();
yield sleep(150); // 約等待150ms
console.log(Date.now() - now);
});
function sleep(ms){
return function(cb){
setTimeout(cb, ms);
};
}
import fetch from 'node-fetch';
co(function* (){
let result = yield [
(yield fetch('https://api.github.com/users/1')).json(),
(yield fetch('https://api.github.com/users/2')).json(),
];
console.log("result:",result);
});
複製代碼
不管是延遲執行,仍是併發的從兩個接口獲取數據,generator均可以用同步的方式編寫異步代碼。
ES7 引入了像C#語言中的 await,async關鍵字,並且babel已支持(引入plugins:transform-async-to-generator )
async函數徹底能夠看做多個異步操做,包裝成的一個Promise對象,而await命令就是內部then命令的語法糖。
import fetch from 'node-fetch';
(async function (){
let result= await fetch('https://api.github.com/users/etoah');
let json =await result.json();
console.log("result:",json);
})();
//exception
(async function (){
try{
let result= await fetch('https://api.3github.com/users/etoah');
let json =await result.json();
console.log("result:",json);
}
catch(ex){
console.warn("warn:",ex);
}
})()
複製代碼
簡單比較會發現,async函數就是將Generator函數的星號(*)替換成async,將yield替換成await,同時不須要co模塊,更加語義化。
可是與yeild又不徹底相同,標準沒有接收await*的語法( :( 查看詳情),
若需「並行」執行promise數組,推薦用Promise.All,因此須要並行請求時,須要這樣寫:
(async function (){
let result= await Promise.all([
(await fetch('https://api.github.com/users/tj')).json(),
(await fetch('https://api.github.com/users/etoah')).json()
]);
console.log("result:",result);
})();
複製代碼
到這裏,jser結合promise,yield,await的寫法,能夠和回調嵌套說拜拜了。