美文网首页bestvist程序员让前端飞
JS中的async/await -- 异步隧道尽头的亮光

JS中的async/await -- 异步隧道尽头的亮光

作者: bestvist | 来源:发表于2018-02-07 15:50 被阅读74次
    async
    JS中的异步操作从最初的回调函数演进到Promise,再到Generator,都是逐步的改进,而async函数的出现仿佛看到了异步方案的终点,用同步的方式写异步。

    原文链接

    async函数

    简单解释async函数就是Generator函数的语法糖。

    Generator函数写法

    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函数不同:

    • 自带内置执行器,Generator函数需要依靠执行器,而async可以和普通函数一样,只需要一行
    • 相对Generator函数,async和await语义更清楚
    • 适用性强,yield后只能是Thunk函数和Promise对象,而await后可以是Promise对象和原始类型的值(数值、字符串、布尔型等)

    async作用

    寄予async函数的期望是希望可以帮助我们解决异步操作问题,所以需要搞清楚async函数的返回值是什么。

    async function asyncAwait() {
        return 'async await';
    }
    
    let a = asyncAwait();
    console.log(a);
    

    结果输出:

    Promise {<resolved>: "async await"}
    

    可以看出async函数返回的是一个Promise对象,如果函数中return一个直接量,async函数会封装成Promise对象返回,而如果没有返回值时,async函数会返回undefined

    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的重要特性之一,也是目前社区里公认的优秀异步解决方案,当你深入了解原理后会发现仿佛看到了异步回调隧道的尽头亮光。

    相关文章

      网友评论

        本文标题:JS中的async/await -- 异步隧道尽头的亮光

        本文链接:https://www.haomeiwen.com/subject/eaqazxtx.html