1. 程式人生 > >async/await 非同步

async/await 非同步

async/await 是一個用同步的思維來解決非同步問題的方案。

async 函式就是 Generator 函式的語法糖。

async 函式的優點

async寫在function前面,該函式返回值是一個promise,可以直接使用then方法。

async function test() {
    return "test";
}

test().then(function(v) {
    console.log(v);
});  //“test”

await只能在async函式中使用。

async function test() {
    return "test";
}

var v = await test();
console.log(v);  //“test”

await不能工作在頂級作用域,需要將await程式碼包裹在一個async函式中

await接受thenables

就像promise.then,await也允許使用thenable物件(那些具有可呼叫的then方法的物件)。同樣,第三方物件可能不是一個promise,但是promise的相容性表示,如果它支援.then方法,那麼它就能用於await。

例如,這裡await接受了new Thenable(1)

class Test {
    constructor(str) {
        this.str= str;
    }
    then(resolve, reject) {
        setTimeout(() => {
            resolve(this.str);
        }, 1000);
   }
}
async function fun() {
    let result = await new Test("abc");
    console.log(result);
}
fun();

如果await得到了一個帶有then方法的非promise物件,它將會呼叫提供原生函式resolve、reject作為引數的方法,然後await一直等待,直到他們其中的一個被呼叫。

async方法
一個class方法同樣能夠使用async,只需要將async放在它之前就可以
就像這樣:

class Waiter {
   async wait () {
       return await Promise.resolve(1)
   }
}
new Waiter().wait().then(alert) // 1

這裡的意思是一樣的:它確保了返回值是一個promise,支援await

async、await序列並行處理

序列:等待前面一個await執行後接著執行下一個await,以此類推

async function test(str) {
     return await new Promise((resolve, reject) => {
         setTimeout(() => {
            resolve(str);
         }, 1000);
     })
 }
 
 var fun = async () => { //序列執行
     console.time('fun');
     console.log(await test('async 1'));
     console.log(await test('async 2'));
     console.log(await test('async 3'));
     console.timeEnd('fun');
}

fun();

可以看到兩個await序列執行的總耗時為兩千多毫秒。

並行:將多個promise直接發起請求(先執行async所在函式),然後再進行await操作。

async function test(str) {
    return await new Promise((resolve, reject) => {
         setTimeout(() => {
            resolve(str);
        }, 1000);
     })
 }
 var fun1 = async () => { //並行執行
     console.time('fun');
     const t1 = test('async 1');
     const t2 = test('async 2');
     const t3 = test('async 3');

     //直接列印
     console.log(await t1);
     console.log(await t2);
     console.log(await t3);

     console.timeEnd('fun');
}

var fun2 = async () => { //序列執行
     console.time('fun2');

     const t1 = test('async 1');
     const t2 = test('async 2');
     const t3 = test('async 3');

     const t = [t1, t2, t3];
     console.log(await Promise.all(t));

     console.timeEnd('fun2');
}

fun1();
fun2();


fun1與fun2寫法效果基本無異

通過列印我們可以看到相對於序列執行,效率提升了一倍。在並行請求中先執行async的非同步操作再await它的結果,把多個序列請求改為並行可以將程式碼執行得更快,效率更高。

錯誤處理

可以使用try-catch語句捕獲錯誤,就像在正常丟擲中處理異常一樣。

如果我們不使用try-catch,然後async函式的呼叫產生的promise變成reject狀態的話,我們可以新增.catch去處理它:

如果我們忘記新增.catch,我們就會得到一個未被處理的promise錯誤(能夠在控制檯裡看到它),這時我們可以通過使用一個全域性的事件處理器去捕獲錯誤。

async function myFun() {
  try {
    await fun();
  } catch (err) {
    console.log(err);
  }
}

// 另一種寫法
async function myFun() {
  await fun().catch(function (err){
    console.log(err);
  });
}

 

總結

放在一個函式前的async有兩個作用:

1)使函式總是返回一個promise

2)允許在這其中使用await

有了async/await,很少需要寫promise.then/catch,但是Promise.all沒有替代方式,它能夠同時等待很多工。

async/await對比Promise優點:

1) 真正地用同步的方式寫非同步程式碼

2) 不用寫then及其回撥函式,減少程式碼行數,也避免了程式碼巢狀

3) 所有非同步呼叫可以寫在同一個程式碼塊中,無需定義多餘的中間變數

4) async函式會隱式地返回一個Promise,因此可以直接return變數,無需使用Promise.resolve進行轉換