ES7的async和await,目前最爲簡略的異步解決方案

隨着js的發展,在解決回調地獄問題的方案上,解決方案逐漸更新,有promise、generator和現在的async都是比較常見的;
1.Promise
這個解決方案就是把異步用同步的方式寫出來,一步一步的.then()方法,前一個.then()執行完之後,繼續執行下一個.then();

function timeout (time) {
    return new Promise((res, rej)=>{
        setTimeout(()=>{
            res('this is a returnDate');
        },time)
    })
};

time(5000)
.then((data)=>{console.log(data)})
.then(()=>{console.log('end')})
...

這就是promise的異步解決方案看起來還是很簡單的,其實只是以你爲邏輯簡單;

2.Generator
Generator函數是個異步管理器,這裏要提到一個協程的概念,先不用管協程是什麼鬼,先來介紹一下yield命令,它是Generator函數中的分水嶺,在函數內部起執行權轉義的功能,什麼是執行權?好吧,這就是在Generator函數內部遇到yield命令,那麼就不往下執行了,就把執行權交出來給別的函數,等別的函數執行之後返回結果了,在返還執行權,繼續向下執行Generator函數的語句;

function generator () {
    // 代碼A;
    var f = yield readFile(fileA);
    // 代碼A;
}

以上代碼中,當函數執行的時候分一下步驟:
a.開始執行代碼A;
b.代碼A執行到一半,遇到yield命令,暫停執行,開始執行,執行權交給yield命令後面的函數;
c.當yield函數執行完之後,交還執行權;
d.繼續執行yield命令下面的代碼A;
在這個過程中,代碼段A被分成兩段執行,代碼段A叫做協程A,readFile()函數叫做協程B,協程A是異步的;
再來舉個例子:

function* gen(x){
    var y = yield x+2;
    return y
}

var g = gen(1);
g.next() // { value:3, done:false }
g.next() // { value:undefined, done:ture }

a.調用Generator函數並不會像其他普通函數一樣返回函數值,它會返回一個內部指針——g;
b.調用內部指針中的next方法,會移動指針分配執行權去手動執行函數;返回結果會是一個對象,value字段對應運算值,done字段是布爾類型,說明函數是否執行完;
c.上面代碼中,第一次調用next方法,函數執行到x+2,值返回給value屬性;第二次調用next方法,執行代碼是return y,這時y並沒有值,只是定義了,所以第二次vlaue的值是undefined
d.當然next方法也是可以傳值的,如下:

var g = gen(1);
g.next() // { value:3, done:false }
g.next(2) // { value:2, done:ture }

這時第二個next方法帶有參數2,這個參數會傳到Generator函數內部,作爲上個協程任務的返回結果被函數體內的變量y接收,因此value的值是2;

對了這裏在補一句,從上可以看出Generator函數是需要手動執行的,那麼有沒有可以讓其自執行的辦法呢,有~
co模塊小工具可以讓Generator函數自執行,只要把其當參數傳進co中;

var co = require('co');
co(gen);

3.async函數
這個es7草案,說實話也是最簡單的異步解決方案,問其原理,其實是Generator函數的語法糖;
a:

function timeout (time) {
    return new Promise((res, rej)=>{
        setTimeout(()=>{
            res();
        },time)
    })
}

let start = async function () {
    console.log('start');
    let data = await timeout(5000);
    console.log('end');
}

執行結果是:
先打印出start,然後轉爲timeout函數執行,執行時間爲5s,之後返回打印出end
是不是跟Generator函數很相似,awaityield命令作用一樣~

b.當然了,也是可以獲取到返回值的:

function timeout (time) {
    return new Promise((res, rej)=>{
        setTimeout(()=>{
            res('this is a returnDate');
        },time)
    })
}

let start = async function () {
    console.log('start');
    let data = await timeout(5000);
    console.log(data);
}

這樣,5s後打印出來的就是this is a returnDate;

c.當然,也可以捕獲錯誤:

function timeout (time) {
    return new Promise((res, rej)=>{
        setTimeout(()=>{
            rej('this is an err');
        },time)
    })
}

let start = async function () {
    console.log('start');
    try {
        let data = await timeout(5000);
        console.log(data);
    } catch (err) {
        console.log(err);
    }

}

這裏timeout函數返回一個錯誤,那麼被try...catch...語句中的catch捕獲,則不執行try中的代碼,直接執行catch中的代碼,打印出錯誤;

d.當然了,這裏也是可以循環多執行await的

function timeout (time) {
    return new Promise((res, rej)=>{
        setTimeout(()=>{
            rej('this is an err');
        },time)
    })
}

let start = async function () {
    for(let i=0; i<=10; i++) {
        console.log(`這是第${i}次等待`);
        await timeout(5000);
    }
}

這裏指出非常重要的一點,await命令必須要在async函數的上下文中,也就是說當await命令所在的環境中this的指向不是async的時候,會報錯!
所以這裏要用for循環,for…of循環,不能用map,forEach方法等;

發佈了32 篇原創文章 · 獲贊 11 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章