美文网首页
2019-08-28 async await 技巧和误区

2019-08-28 async await 技巧和误区

作者: KingAmo | 来源:发表于2019-08-28 14:06 被阅读0次

    async函数的返回值是 Promise 对象

    怎么理解上面这句话呢?我们知道,普通的函数的执行结果要有返回值的话,需要用return关键字,没有return关键字则返回值是 undefined

      const f = () => {
        doSomething()...
      }
    
      const res1 = f() // undefined
    
      const g = () => {
        doSomething()...
        return 'hello world'
      }
    
    const res2 = g() // 'hello world'
    

    但是async 函数不一样,async函数一定有返回值,返回值是一个promise 对象;这是怎么做到的呢?async函数中的 return 关键字有什么用呢?

    • 如果async函数中没有 return 语句,async函数会自动返回Promise.resolve(undefined),于是就返回了一个Promise对象

    • 如果async函数return一个直接量xxx,async函数会自动返回Promise.resolve(xxx),于是返回的Promise对象的状态是resolved。在调用async函数后,接收 return 返回的值只能用then方法。

    async function testAsync() {
        const res = await fetch('xxx') //返回`hello async`
        doSomething()...
        return res;
    }
    
    testAsync().then(v => {
        console.log(v);    // 输出 hello async
    });
    console.log('123')
    

    async函数内部,await后面如果是一个 Promise 对象,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果,然后继续向下执行。
    async函数外部,调用async函数后,无论里面是否有耗时操作,都会立即返回,继续向下执行。所以,上面代码,会先输出123,再输出hello async

    async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误
    async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被外面添加的catch方法回调函数接收到。
    任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。

    如果我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。(ps:因为async函数里面有try catch捕获了异常,没有抛到外面来,所以外面async函数返回的promise不会被reject)

    如果await后面的异步操作出错,又没有在async函数内被我们主动catch,那么async函数返回的 Promise 对象就会被reject。

    最佳实践

    async await 本来就是来替代 promise ...then...的,写成同步的语法,比一连串的then简洁很多,但是,async 返回的还是一个promise对象,怎么处理这里的矛盾呢?
    我的建议是,在async函数中,加入try catch,所有的逻辑写在try catch中,并且一般情况下不需要return xxx,直接把后续逻辑在async函数中写完,而不是return xxx后,在 then中写后续逻辑;不过这样一来,发生错误后,被里面的catch捕获了,返回的promise仍然是resolved,如果要在外层catch中处理错误,而不是在async中处理错误,可以在里面catch到错误后,调用Promise.reject()来手动让外层catch到错误。

    show me code :

    const f1 = async () => {
        console.log('第一个function start');
        const res = await new Promise((resolve) => {
            setTimeout(() => {
                console.log('第一个async');
                resolve('f1 resolved');
            }, 3000);
        });
        console.log('第一个function end');
        return res;
    };
    const f2 = async () => {
        console.log('第二个function start');
        const res = await new Promise((resolve) => {
            setTimeout(() => {
                console.log('第二个async');
                resolve('f2 resolved');
            }, 3000);
        });
        console.log('第二个function end');
        return res;
    };
    
    const test1 = () => {
        const res1 = f1();
        const res2 = f2();
        console.log(123456789, res1, res2);
    };
    test1();
    // output:
    // 同时:
    // 第一个function start
    // 第二个function start
    // 123456789,  两个 promise 对象
    // 3s后 同时:
    // 第一个async
    // 第二个async
    // 第一个function end
    // 第二个function end
    // 这里 `f1` 和 `f2` 是同步执行的
    

    const test2 =  () => {
        f1().then(res => {console.log(res);});
        f2().then(res => {console.log(res);});
        console.log(123456789);
    };
    test2();
    // output:
    // 同时:
    // 第一个function start
    // 第二个function start
    // 123456789,  两个 promise 对象
    // 3s后 同时:
    // 第一个async
    // 第二个async
    // 第一个function end
    // 第二个function end
    // f1 resolved
    // f2 resolved
    // 和上面一样 这里 `f1` 和 `f2` 是同步执行的
    

    const test3 = async () => {
        const res1 = await f1();
        const res2 = await f2();
        console.log(123456789, res1, res2);
    };
    test3();
    // output: 
    // 立即
    // 第一个function start
    // 3s 后 同时打印:
    // 第一个async
    // 第一个function end
    // 第二个function start
    // 再3s后
    // 第二个async
    // 第二个function end
    // 123456789 "f1 resolved" "f2 resolved"
    

    总结: async 函数抽出后,要想执行 async 函数时是等待阻塞的,还得在外面嵌套async函数, 因为 async 函数返回的还是 Promise 对象

    相关文章

      网友评论

          本文标题:2019-08-28 async await 技巧和误区

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