JS中的async/await -- 異步隧道盡頭的亮光

JS中的異步操做從最初的回調函數演進到Promise,再到Generator,都是逐步的改進,而async函數的出現彷彿看到了異步方案的終點,用同步的方式寫異步。
asyncajax

原文連接promise

async函數

簡單解釋async函數就是Generator函數的語法糖。異步

Generator函數寫法async

let promise = function (val){
    return new Promise(function (resolve, reject){
        setTimeout(()=>{
            console.log(val);
            resolve(val);
        },1000);
    });
};

let gen = function* (){
    let p1 = yield promise('1');
    let p2 = yield promise('2');
};

let genF = gen();

async函數寫法函數

let promise = function (val){
    return new Promise(function (resolve, reject){
        setTimeout(()=>{
            console.log(val);
            resolve(val);
        },1000);
    });
};

let gen = async function (){
    let p1 = await promise('1');
    let p2 = await promise('2');
};

async函數是在Generator函數上進行的改進,語法上Generator函數的星號換成了async,yield換成了await。
而async也與Generator函數不一樣:spa

  • 自帶內置執行器,Generator函數須要依靠執行器,而async能夠和普通函數同樣,只須要一行
  • 相對Generator函數,async和await語義更清楚
  • 適用性強,yield後只能是Thunk函數和Promise對象,而await後能夠是Promise對象和原始類型的值(數值、字符串、布爾型等)

async做用

寄予async函數的指望是但願能夠幫助咱們解決異步操做問題,因此須要搞清楚async函數的返回值是什麼。code

async function asyncAwait() {
    return 'async await';
}

let a = asyncAwait();
console.log(a);

結果輸出:對象

Promise {<resolved>: "async await"}

能夠看出async函數返回的是一個Promise對象,若是函數中return一個直接量,async函數會封裝成Promise對象返回,而若是沒有返回值時,async函數會返回undefinedrem

Promise {<resolved>: undefined}

在沒有結合await時,async函數會當即執行,返回一個Promise對象。字符串

await等待

await是個運算符,等待的結果是Promise對象或其餘值,好比:

function func1() {
    return 'async';
}

async function func2() {
    return Promise.resolve('await');
}

async function asyncAwait() {
    let f1 = await func1();
    let f2 = await func2();
    console.log(f1, f2);
}

asyncAwait()

結果輸出:

async await

await表達式的運算取決於等待的結果,若是它等到的不是一個Promise對象,那運算結果就是它等到的東西,
而若是它等到的是一個Promise對象,它會阻塞後面的代碼,等着Promise對象resolve,而後獲得resolve的值,做爲表達式的運算結果。
async函數調用會封裝在Promise中,這也是await須要在async函數中使用的緣由。

async/await鏈式處理

對於多個異步操做中,Promise的then能夠解決多層回調問題。

function ajax(t) {
    return new Promise(resolve => {
        setTimeout(() => resolve(t + 200), t);
    });
}

function step1(t) {
    console.log(`step1 in ${t}ms`);
    return ajax(t);
}

function step2(t) {
    console.log(`step2 in ${t}ms`);
    return ajax(t);
}

function step3(t) {
    console.log(`step3 in ${t}ms`);
    return ajax(t);
}

function submit(){
    console.time('submit');
    step1(200)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {
            console.log(`result is ${result}ms`);
            console.timeEnd("submit");
        });
}

submit();

async函數實現:

function ajax(t) {
    return new Promise(resolve => {
        setTimeout(() => resolve(t + 200), t);
    });
}

function step1(t) {
    console.log(`step1 in ${t}ms`);
    return ajax(t);
}

function step2(t) {
    console.log(`step2 in ${t}ms`);
    return ajax(t);
}

function step3(t) {
    console.log(`step3 in ${t}ms`);
    return ajax(t);
}

async function submit(){
    console.time('submit');
    const t1 = 200;
    const t2 = await step1(t1);
    const t3 = await step2(t2);
    const result = await step3(t3);
    console.log(`result is ${result}`);
    console.timeEnd('submit');
}

submit();

結果輸出:

step1 in 200ms
step2 in 400ms
step3 in 600ms
result is 800
submit: 1209.85107421875ms

而若是需求變動,每一步的參數都是以前步驟的結果後,async函數能夠寫成:

function ajax(t) {
    return new Promise(resolve => {
        setTimeout(() => resolve(t + 200), t);
    });
}

function step1(t1) {
    console.log(`step1 in ${t1}ms`);
    return ajax(t1);
}

function step2(t1, t2) {
    console.log(`step2 in ${t1}ms,${t2}ms`);
    return ajax(t1 + t2);
}

function step3(t1, t2, t3) {
    console.log(`step3 in ${t1}ms,${t2}ms,${t3}ms`);
    return ajax(t1 + t2 + t3);
}

async function submit(){
    console.time('submit');
    const t1 = 200;
    const t2 = await step1(t1);
    const t3 = await step2(t1, t2);
    const result = await step3(t1, t2, t3);
    console.log(`result is ${result}`);
    console.timeEnd('submit');
}

submit();

結果輸出:

step1 in 200ms
step2 in 200ms,400ms
step3 in 200ms,400ms,800ms
result is 1600
submit: 2210.47998046875ms

async/await注意點

  • async用來申明裏麪包裹的內容能夠進行同步的方式執行,await則是進行執行順序控制,每次執行一個await,阻塞代碼執行等待await返回值,而後再執行以後的await。
  • await後面調用的函數須要返回一個promise。
  • await只能用在async函數之中,用在普通函數中會報錯。
  • await命令後面的Promise對象,運行結果多是rejected,因此最好把await命令放在try...catch代碼塊中。

async/await try/catch寫法

async function asyncAwait() {
    try {
        await promise();
    } catch (err) {
        console.log(err);
    }
}

// 另外一種寫法
async function asyncAwait() {
    await promise().catch(function (err){
        console.log(err);
    });
}

總結

async/await是ES7的重要特性之一,也是目前社區裏公認的優秀異步解決方案,當你深刻了解原理後會發現彷彿看到了異步回調隧道的盡頭亮光。

相關文章
相關標籤/搜索