async/await
ES2017 提出的async 函數,終於讓 JavaScript 對於異步操作有了終極解決方案。No more callback hell。
-
async 函數是 Generator 函數的語法糖。使用 關鍵字 async 來表示,在函數內部使用 await 來表示異步。
想較於 Generator,Async 函數的改進在於下面四點: -
內置執行器。Generator 函數的執行必須依靠執行器,而 Aysnc 函數自帶執行器,調用方式跟普通函數的調用一樣;
-
更好的語義。async 和 await 相較於 * 和 yield 更加語義化;
-
更廣的適用性。co 模塊約定,yield 命令後面只能是 Thunk 函數或 Promise對象。而 async 函數的 await命令後面則可以是 Promise 或者 原始類型的值(Number,string,boolean,但這時等同於同步操作);
-
返回值是 Promise。async 函數返回值是 Promise 對象,比 Generator 函數返回的 Iterator對象方便,可以直接使用 then() 方法進行調用。
//promise實例
const makeRequest = ()=>{
return getJson()
.then(data=>{
if(data.needsAnotherRequest){
return makeAnotherRequest(data)
.then(moreData=>{
console.log(moreData);
return moreData;
})
}else{
console.log(data);
return data;
}
})
}
//async/await
const makeRequest = async()=>{
const data = await getJson()
if(data.needsAnotheRRequest){
const moreData = await makeAnotherRequest(data);
console.log(moreData);
return moreData;
}else{
console.log(data);
return data;
}
}
阮一峯大神的ES6入門
返回thenable對象
class sleep{
constructor(timeout){
this.timeout = timeout;
}
then(resolve,reject){
const startTime = Date.now();
setTimeout(
()=>resolve(Date.now()-startTime),
this.timeout
);
}
}
(async ()=>{
const sleep = await new Sleep(1000);
console.log(sleepTime);
})();
javascript 休眠
function sleep(interval) {
return new Promise(resolve => {
setTimeout(resolve, interval);
})
}
// 用法
async function one2FiveInAsync() {
for(let i = 1; i <= 5; i++) {
console.log(i);
await sleep(1000);
}
}
one2FiveInAsync();
任何一個await語句後面的 Promise 對象變爲reject狀態,那麼整個async函數都會中斷執行。
async function f() {
await Promise.reject('出錯了');
await Promise.resolve('hello world'); // 不會執行
}
錯誤處理
如果await後面的異步操作出錯,那麼等同於async函數返回的 Promise 對象被reject。
async function f(){
await new Promise(function(resolve,reject){
throw new Error("出錯了");
});
}
f()
.then(v=>console.log(v))
.catch(e=>console.log(e))
//Error 出錯了
防止出錯的方法,也是將其放在try…catch代碼塊之中。
async function f(){
try{
await new Promiseh(function(resolve,reject){
throw new Error('出錯了');
});
}catch(e){
}
return await('hello world');
}
實現原理
將Generator函數和自動執行器包裝在一個函數中
async function fn(args){
//something to do
}
function fn(args){
return spawn(function* (){
//
});
}
spawn 是自動執行器 簡單實現如下:
function spawn(genF) {
return new Promise(function(resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch(e) {
return reject(e);
}
if(next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
手寫Async
function asyncToGenerator(generatorFunc) {
// 返回的是一個新的函數
return function() {
// 先調用generator函數 生成迭代器
// 對應 var gen = testG()
const gen = generatorFunc.apply(this, arguments)
// 返回一個promise 因爲外部是用.then的方式 或者await的方式去使用這個函數的返回值的
// var test = asyncToGenerator(testG)
// test().then(res => console.log(res))
return new Promise((resolve, reject) => {
// 內部定義一個step函數 用來一步一步的跨過yield的阻礙
// key有next和throw兩種取值,分別對應了gen的next和throw方法
// arg參數則是用來把promise resolve出來的值交給下一個yield
function step(key, arg) {
let generatorResult
// 這個方法需要包裹在try catch中
// 如果報錯了 就把promise給reject掉 外部通過.catch可以獲取到錯誤
try {
generatorResult = gen[key](arg)
} catch (error) {
return reject(error)
}
// gen.next() 得到的結果是一個 { value, done } 的結構
const { value, done } = generatorResult
if (done) {
// 如果已經完成了 就直接resolve這個promise
// 這個done是在最後一次調用next後纔會爲true
// 以本文的例子來說 此時的結果是 { done: true, value: 'success' }
// 這個value也就是generator函數最後的返回值
return resolve(value)
} else {
// 除了最後結束的時候外,每次調用gen.next()
// 其實是返回 { value: Promise, done: false } 的結構,
// 這裏要注意的是Promise.resolve可以接受一個promise爲參數
// 並且這個promise參數被resolve的時候,這個then纔會被調用
return Promise.resolve(
// 這個value對應的是yield後面的promise
value
).then(
// value這個promise被resolve的時候,就會執行next
// 並且只要done不是true的時候 就會遞歸的往下解開promise
// 對應gen.next().value.then(value => {
// gen.next(value).value.then(value2 => {
// gen.next()
//
// // 此時done爲true了 整個promise被resolve了
// // 最外部的test().then(res => console.log(res))的then就開始執行了
// })
// })
function onResolve(val) {
step("next", val)
},
// 如果promise被reject了 就再次進入step函數
// 不同的是,這次的try catch中調用的是gen.throw(err)
// 那麼自然就被catch到 然後把promise給reject掉啦
function onReject(err) {
step("throw", err)
},
)
}
}
step("next")
})
}
}