歡迎留言討論
chrome
async
函數是 Generator
函數的語法糖。使用 關鍵字 async
來表示,在函數內部使用 await
來表示異步。相較於 Generator
,async
函數的改進在於下面四點:編程
內置執行器。Generator
函數的執行必須依靠執行器,而 async
函數自帶執行器,調用方式跟普通函數的調用同樣segmentfault
更好的語義。async
和 await
相較於 *
和 yield
更加語義化promise
更廣的適用性。co
模塊約定,yield
命令後面只能是 Thunk 函數或 Promise對象。而 async
函數的 await
命令後面則能夠是 Promise 或者 原始類型的值(Number,string,boolean,但這時等同於同步操做)瀏覽器
返回值是 Promise。async
函數返回值是 Promise 對象,比 Generator 函數返回的 Iterator 對象方便,能夠直接使用 then()
方法進行調用bash
此處總結參考自:理解async/awaitapp
async
是ES7新出的特性,代表當前函數是異步函數,不會阻塞線程致使後續代碼中止運行。異步
申明以後就能夠進行調用了async
async function asyncFn() {
return 'hello world';
}
asyncFn();複製代碼
這樣就表示這是異步函數,返回的結果函數
async 表示函數裏有異步操做
await 表示緊跟在後面的表達式須要等待結果。
返回的是一個promise
對象,狀態爲resolved
,參數是return
的值。那再看下面這個函數
async function asyncFn() {
return '我後執行'
}
asyncFn().then(result => {
console.log(result);
})
console.log('我先執行');複製代碼
上面的執行結果是先打印出'我先執行'
,雖然是上面asyncFn()
先執行,可是已經被定義異步函數了,不會影響後續函數的執行。
如今理解了async
基本的使用,那還有什麼特性呢?
async
定義的函數內部會默認返回一個promise
對象,若是函數內部拋出異常或者是返回reject
,都會使函數的promise
狀態爲失敗reject
。
async function e() {
throw new Error('has Error');
}
e().then(success => console.log('成功', success))
.catch(error => console.log('失敗', error));複製代碼
咱們看到函數內部拋出了一個異常
,返回reject
,async
函數接收到以後,斷定執行失敗進入catch
,該返回的錯誤打印了出來。
async function throwStatus() {
return '能夠返回全部類型的值'
}
throwStatus().then(success => console.log('成功', success))
.catch(error => console.log('失敗', error));複製代碼
//打印結果
成功 能夠返回全部類型的值
複製代碼
async
函數接收到返回的值,發現不是異常
或者reject
,則斷定成功,這裏能夠return
各類數據類型的值,false
,NaN
,undefined
...總之,都是resolve
可是返回以下結果會使async
函數斷定失敗reject
throw new Error
或者返回reject
狀態return Promise.reject('執行失敗')
還有一點,在async
裏,必需要將結果return
回來,否則的話不論是執行reject
仍是resolved
的值都爲undefine
,建議使用箭頭函數。
其他返回結果都是斷定resolved
成功執行。
//正確reject方法。必須將reject狀態return出去。
async function PromiseError() {
return Promise.reject('has Promise Error');
}
//這是錯誤的作法,而且斷定resolve,返回值爲undefined,而且Uncaught報錯
async function PromiseError() {
Promise.reject('這是錯誤的作法');
}
PromiseError().then(success => console.log('成功', success))
.catch(error => console.log('失敗', error));複製代碼
咱們看到第二行多了個Promise
對象打印,不用在乎,這個是在Chrome
控制檯的默認行爲,咱們日常在控制檯進行賦值也是一樣的效果。若是最後執行語句
或者表達式
沒有return
返回值,默認undefined
,作個小實驗。
var a = 1;
//undefined
------------------------------------------------------------
console.log(a);
//1
//undefined
------------------------------------------------------------
function a(){ console.log(1) }
a();
//1
//undefined
------------------------------------------------------------
function b(){ return console.log(1) }
b();
//1
//undefined
------------------------------------------------------------
function c(){ return 1}
c();
//1
------------------------------------------------------------
async function d(){
'這個值接收不到'
}
d().then(success => console.log('成功',success));
//成功 undefined
//Promise { <resolved>: undefined }
-----------------------------------------------------------
async function e(){
return '接收到了'
}
e().then(success => console.log('成功',success));
//成功 接收到了
//Promise { <resolved>: undefined }複製代碼
最後一行Promise { <resolved> : undefined }
是由於返回的是console.log
執行語句,沒有返回值。
d().then(success => console.log('成功',success)}
等同於
d().then(function(success){
return console.log('成功',success);
});
複製代碼
js自己是單線程的,經過v8咱們能夠擁有"異步"的能力
認識完了async,來說講await。
await
意思是async wait(異步等待)。這個關鍵字只能在使用async
定義的函數裏面使用。任何async
函數都會默認返回promise
,而且這個promise
解析的值都將會是這個函數的返回值,而async
函數必須等到內部全部的 await
命令的 Promise
對象執行完,纔會發生狀態改變。
打個比方,await是學生,async是校車,必須等人齊了再開車。
await
函數執行完畢後,纔會告訴
promise
我成功了仍是失敗了,執行
then
或者
catch
async function awaitReturn() {
return await 1
};
awaitReturn().then(success => console.log('成功', success))
.catch(error => console.log('失敗',error))複製代碼
在這個函數裏,有一個await
函數,async會等到await 1
這一步執行完了纔會返回promise
狀態,毫無疑問,斷定resolved
。
不少人覺得await
會一直等待以後的表達式執行完以後纔會繼續執行後面的代碼,實際上await
是一個讓出線程的標誌。await
後面的函數會先執行一遍(好比await Fn()的Fn ,並不是是下一行代碼),而後就會跳出整個async
函數來執行後面js棧的代碼。等本輪事件循環執行完了以後又會跳回到async
函數中等待await後面表達式的返回值,若是返回值爲非promise
則繼續執行async
函數後面的代碼,不然將返回的promise
放入Promise
隊列(Promise的Job Queue)
來看個簡單點的例子
const timeoutFn = function(timeout){
return new Promise(function(resolve){
return setTimeout(resolve, timeout);
});
}
async function fn(){
await timeoutFn(1000);
await timeoutFn(2000);
return '完成';
}
fn().then(success => console.log(success));複製代碼
這裏本能夠用箭頭函數寫方便點,可是爲了便於閱讀本質,仍是換成了ES5寫法,上面執行函數內全部的await函數纔會返回狀態,結果是執行完畢3秒後纔會彈出'完成
'。
正常狀況下,await 命令後面跟着的是 Promise ,若是不是的話,也會被轉換成一個 當即 resolve 的 Promise。
也能夠這麼寫
function timeout(time){
return new Promise(function(resolve){
return setTimeout(function(){
return resolve(time + 200)
},time);
})
}
function first(time){
console.log('第一次延遲了' + time );
return timeout(time);
}
function second(time){
console.log('第二次延遲了' + time );
return timeout(time);
}
function third(time){
console.log('第三次延遲了' + time );
return timeout(time);
}
function start(){
console.log('START');
const time1 = 500;
first(time1).then(time2 => second(time2) )
.then(time3 => third(time3) )
.then(res => {
console.log('最後一次延遲' + res );
console.timeEnd('END');
})
};
start();複製代碼
這樣用then鏈式回調的方式執行resolve
//打印結果
START
第一次延遲了500
第二次延遲了700
第三次延遲了900
最後一次延遲1100
END
複製代碼
用async/await呢?
async function start() {
console.log('START');
const time1 = 500;
const time2 = await first(time1);
const time3 = await second(time2);
const res = await third(time3);
console.log(`最後一次延遲${res}`);
console.log('END');
}
start();複製代碼
達到了相同的效果。可是這樣遇到一個問題,若是await
執行遇到報錯呢
async function start() {
console.log('START');
const time1 = 500;
const time2 = await first(time1);
const time3 = await Promise.reject(time2);
const res = await third(time3);
console.log(`最後一次延遲${res}`);
console.log('END');
}
start();複製代碼
返回reject後,後面的代碼都沒有執行了,以此遷出一個例子:
let last;
async function throwError() {
await Promise.reject('error');
last = await '沒有執行';
}
throwError().then(success => console.log('成功', last))
.catch(error => console.log('失敗',last))複製代碼
其實
async
函數不難,難在錯處理上。
上面函數,執行的到await
排除一個錯誤後,就中止往下執行,致使last
沒有賦值報錯。
async
裏若是有多個await函數的時候,若是其中任一一個拋出異常或者報錯了,都會致使函數中止執行,直接
reject
;
怎麼處理呢,能夠用try/catch
,遇到函數的時候,能夠將錯誤拋出,而且繼續往下執行。
let last;
async function throwError() {
try{
await Promise.reject('error');
last = await '沒有執行';
}catch(error){
console.log('has Error stop');
}
}
throwError().then(success => console.log('成功', last))
.catch(error => console.log('失敗',last))複製代碼
這樣的話,就能夠繼續往下執行了。
來個🌰練習下
function testSometing() {
console.log("testSomething");
return "return testSomething";
}
async function testAsync() {
console.log("testAsync");
return Promise.resolve("hello async");
}
async function test() {
console.log("test start...");
const testFn1 = await testSometing();
console.log(testFn1);
const testFn2 = await testAsync();
console.log(testFn2);
console.log('test end...');
}
test();
var promiseFn = new Promise((resolve)=> {
console.log("promise START...");
resolve("promise RESOLVE");
});
promiseFn.then((val)=> console.log(val));
console.log("===END===")複製代碼
執行結果
咱們一步步來解析
首先test()
打印出test start...
而後 testFn1 = await testSomething();
的時候,會先執行testSometing()
這個函數打印出「testSometing
」的字符串。
以後由於await
會讓出線程就會去執行後面的。testAsync()
執行完畢返回resolve
,觸發promiseFn
打印出「promise START...
」。
接下來會把返回的Promiseresolve("promise RESOLVE")
放入Promise隊列(Promise的Job Queue),繼續執行打印「===END===
」。
等本輪事件循環執行結束後,又會跳回到async
函數中(test()
函數),等待以前await
後面表達式的返回值,由於testSometing()
不是async
函數,因此返回的是一個字符串「return
testSometing
」。
test()
函數繼續執行,執行到testFn2()
,再次跳出test()
函數,打印出「testAsync
」,此時事件循環就到了Promise的隊列,執行promiseFn.then((val)=> console.log(val));
打印出「promise RESOLVE
」。
以後和前面同樣 又跳回到test函數繼續執行console.log(testFn2)
的返回值,打印出「hello async
」。
最後打印「test end...
」。
加點料,讓testSomething()
變成async
async function testSometing() {
console.log("testSomething");
return "return testSomething";
}
async function testAsync() {
console.log("testAsync");
return Promise.resolve("hello async");
}
async function test() {
console.log("test start...");
const testFn1 = await testSometing();
console.log(testFn1);
const testFn2 = await testAsync();
console.log(testFn2);
console.log('test end...');
}
test();
var promiseFn = new Promise((resolve)=> {
console.log("promise START...");
resolve("promise RESOLVE");
});
promiseFn.then((val)=> console.log(val));
console.log("===END===")複製代碼
執行結果
promiseFn.then((val)=> console.log(val));
先於console.log(testFn1)
執行。
緣由是由於如今的版本async函數會被await resolve,
function testSometing() {
console.log("testSomething");
return "return testSomething";
}
console.log(Object.prototype.toString.call(testSometing)) // [object Function]
console.log(Object.prototype.toString.call(testSometing())) // [object String]
async function testSometing() {
console.log("testSomething");
return "return testSomething";
}
console.log(Object.prototype.toString.call(testSometing)) // [object AsyncFunction]
console.log(Object.prototype.toString.call(testSometing())) // [object Promise]複製代碼
testSomething()
已是async函數,返回的是一個Promise對象須要等它 resolve 後將當前Promise 推入隊列,隨後先清空調用棧,因此會"跳出" test() 函數執行後續代碼,隨後纔開始執行該 Promise。
今天是2019-11-06 12:03:46,收到幾位讀者的反饋,最後一個例子的執行時機不符合現有版本的result,緣由多是在文章完結後至今的一年半內 Chrome V8 更新致使,若是有 Opear 或者是其餘使用了 Chrome 內核的瀏覽器,或者到 這個地址 下載舊版本 Chrome,筆者測試使用的是 Opera,在 Chrome V8 release 文檔或裏並未找到相關說明。
在當前的版本,async 函數不須要等待被 await resolve了,由於正常狀況下它們最終返回都終將是Promise。
而現版本 await AsyncFunction 返回 Promise 的優先級相似同步任務,高於普通 Promise 的 promiseFullfil 的優先級 `promiseFn.then((val)=> console.log(val));` 有興趣的能夠看下這篇關於解析V8 的文章。
愈來愈多的人正在研究聽說是異步終極編程解決方案的async/await,可是大部分人對這個方法內部怎麼執行的還不是很瞭解,整理了await以後js的執行順序,但願對大家有所幫助