Async

Promise虽然解决了地狱回调的问题,但是它的写法似乎不够优雅,在一些复杂的逻辑情况下也会显得代码冗余。
所以,出现了生成器,生成器可在函数内部停止执行,这意味着可把它们封装在一个多用途的函数中,我们可在代码移动到下一行之前等待异步操作完成。突然你的异步代码可能就开始看起来同步了,例如Q库。

var doAsyncOp = Q.async(function* () {
    var val = yield asynchronousOperation();
    console.log(val);
    return val;
});

Q.async 是个封装函数,处理场景后的事情。其中 * 表示作为一个生成器函数的功能,yield 表示停止函数,并用封装函数代替。Q.async 将会返回一个函数,你可对它赋值,就像赋值 doAsyncOp 一样,随后再调用。

而ES7中async的写法更加的简介:

async function doAsyncOp () {
    var val = await asynchronousOperation();     
    console.log(val);
    return val;
};

用async来定义一个异步函数,也不用使用奇怪的*号了,使用await代替yield。

如何将Promise转为异步函数

先看一下下面的Promise写法,先定义一个异步操作,间隔多少时间某个数增加1。

function asyTimeOut(time, val){
    return new Promise(function (resolve) {
        setTimeout(function() {
            console.log("延迟"+time/1000+"秒输出...");
            resolve(++val);
        }, time)
    })
}

promise:

function doAsyncOP() {
    var prm = asyTimeOut(1000, 10)
        .then((val)=>{
            console.log(val);
        })
    return prm;
}
doAsyncOP();

async:

async function doAsyncOp() {
    var prm = await asyTimeOut(3000, 10);
    console.log(val);
    return prm;
}
doAsyncOp();

链式操作

promise:

function doAsyncOp2() {
    return asyTimeOut(3000, 10)
        .then(function(val) {
            console.log(val);
            return asyTimeOut(3000, val);
        })
        .then(function(val) {
            console.log(val);
            return asyTimeOut(3000, val);
        })
        .then(function(val) {
            console.log(val);
            return asyTimeOut(3000, val);
        });
}
doAsyncOp2();

async:

async function doAsyncOp2() {
    console.log("开始...");
    var val = await asyTimeOut(3000, 10);
    console.log(val);
    val = await asyTimeOut(3000, val);
    console.log(val);
    val = await asyTimeOut(3000, val);
    console.log(val);
    return val;
}
var val = doAsyncOp2();

并发操作

promise:

function doAsyncOp3() {
    return Promise.all([
        asyTimeOut(3000,10),
        asyTimeOut(3000,15)
    ]).then(function(vals) {
        vals.forEach(console.log);
        return vals;
    });
}
doAsyncOp3();

async:

async function doAsyncOp3() {
    var list = await Promise.all([
        asyTimeOut(3000,10),
        asyTimeOut(3000,15)]);
    list.forEach(console.log);
}
doAsyncOp3();

处理拒绝 和 中断

在Promise中使用reject来进行处理拒绝,使用new Error()来获得错误信息。

function asyTimeOut(time, val){
    return new Promise(function (resolve, reject) {
        setTimeout(function() {
            console.log("延迟"+time/1000+"秒输出...");
            val < 10?resolve(++val):reject(new Error("I am an error"))
        }, time)
    })
}

promise:

调用catch来捕获错误信息

async:

使用try catch

async function doAsyncOp4 () {
    try {
        var val = await asyTimeOut(3000, 10);
        val = await asyTimeOut(3000, val);
        return await asyTimeOut(3000, val);
    } catch (err) {
        console.log(err);
    }
};

函数嵌套问题

若在async函数中嵌套了其它函数,比如回调,那么回调中的await将会无效

async function doAsyncOp5() {
    return Promise.all(
        [1000,2000].map(function(time) {
            var file = await asyTimeOut(time, 10); // 这个await没有效果,报错Unexpected identifier
            return file;
        })
    );
}
doAsyncOp5();

应该保证await在async下一级的作用域下。

async function doAsyncOp5() {
    return Promise.all(
        [1000,2000].map(async function(time) {
            var p = await asyTimeOut(time, 10);
            return p;
        })
    );
}
doAsyncOp5();

因为async无论如何都会返回一个promise,所以这样写是没问题的。

总结

总的来说,async把promise进行了封装,它让javascript可以更加优雅的实现同异步编程。