资讯专栏INFORMATION COLUMN

细说async/await相较于Promise的优势

J4ck_Chan / 2706人阅读

摘要:它细说了什么是函数,以及其相较于的优势。从最早的回调函数,到对象,再到函数,每次都有所改进,但又让人觉得不彻底。所以,不能把它和回调函数搭配使用。但不用写及其回调函数,这减少代码行数,也避免了代码嵌套。总结的异步编写方式,从回调函数到再到。

前言

介于上一篇 「今日头条」前端面试题和思路解析 中提到的 async/await,让我想起了之前写过的一篇文章,在此做个分享。它细说了什么是async函数,以及其相较于 Promise 的优势。

温故而知新,正文开始。

async 函数是什么?

谈及异步回调函数的嵌套,总会让人感到烦躁,特别是当业务逻辑复杂,往往需要调用几次 ajax 才能拿到所有需要的数据。

从最早的回调函数,到 Promise 对象,再到 Generator 函数,每次都有所改进,但又让人觉得不彻底。它们都有额外的复杂性,都需要理解抽象的底层运行机制。所以,我们需要一种方法,更优雅地解决异步操作。于是,async函数出现了。

一句话解释:async 函数,就是 Generator 函数的语法糖。

它有以下几个特点:

建立在promise之上。所以,不能把它和回调函数搭配使用。但它会声明一个异步函数,并隐式地返回一个Promise。因此可以直接return变量,无需使用Promise.resolve进行转换。

和promise一样,是非阻塞的。但不用写 then 及其回调函数,这减少代码行数,也避免了代码嵌套。而且,所有异步调用,可以写在同一个代码块中,无需定义多余的中间变量。

它的最大价值在于,可以使异步代码,在形式上,更接近于同步代码。

它总是与await一起使用的。并且,await 只能在 async 函数体内。

await 是个运算符,用于组成表达式,它会阻塞后面的代码。如果等到的是 Promise 对象,则得到其 resolve值。否则,会得到一个表达式的运算结果。

为何说 async 函数是语法糖

async 函数的实现,其实就是将 Generator 函数和自动执行器,包装在一个函数里。下面的这个例子,来自阮老师的 《async 函数的含义和用法》 一文。

async function fn(args) {
    // ...
}

// 等同于
function fn(args) {
    return spawn(function*() {
        // ...
    });
}

// spawn 函数就是自动执行器
function spawn(genF) {
    return new Promise(function(resolve, reject) {
        var gen = genF();
        function step(nextF) {
            try {
                var next = nextF();
            } catch(e) {
                return reject(e); 
        }
        if(next.done) {
            return resolve(next.value);
        } 
        Promise.resolve(next.value).then(function(v) {
            step(function() { return gen.next(v); });      
        }, function(e) {
            step(function() { return gen.throw(e); });
        });
    }
    step(function() { return gen.next(undefined); });
  });
}

所以说,async 函数是 Generator 函数的语法糖。另外,它相对较新,属于ES7中的语法。但是转码器 Babel 已经支持,转码后就能使用。

async 相较于 Promise 的优势 1.相比于 Promise,它能更好地处理then链
function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}

function step3(n) {
    console.log(`step3 with ${n}`);
    return takeLongTime(n);
}

现在用 Promise 方式来实现这三个步骤的处理。

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {
            console.log(`result is ${result}`);
        });
}
doIt();
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900

如果用 async/await 来实现的话,会是这样:

async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
}
doIt();

结果和之前的 Promise 实现是一样的,但是这个代码看起来是不是清晰得多,几乎跟同步代码一样。

2.中间值

现在把业务要求改一下,仍然是三个步骤,但每一个步骤都需要之前每个步骤的结果。Pomise的实现看着很晕,传递参数太过麻烦。

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => {
            return step2(time1, time2)
                .then(time3 => [time1, time2, time3]);
        })
        .then(times => {
            const [time1, time2, time3] = times;
            return step3(time1, time2, time3);
        })
        .then(result => {
            console.log(`result is ${result}`);
        });
}
doIt();

用 async/await 来写:

async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time1, time2);
    const result = await step3(time1, time2, time3);
    console.log(`result is ${result}`);
}
doIt();

没有多余的中间值,更加优雅地实现了。

3.调试

相比于 Promise 更易于调试。

因为没有代码块,所以不能在一个返回的箭头函数中设置断点。如果你在一个 .then 代码块中使用调试器的步进(step-over)功能,调试器并不会进入后续的 .then 代码块,因为调试器只能跟踪同步代码的每一步。

现在,如果使用 async/await,你就不必再使用箭头函数。你可以对 await 语句执行步进操作,就好像他们都是普通的同步语句一样。

总结

JavaScript的异步编写方式,从 回调函数 到 Promise、Generator 再到 Async/Await。表面上只是写法的变化,但本质上则是语言层的一次次抽象。让我们可以用更简单的方式实现同样的功能,而不需要去考虑代码是如何执行的。

换句话说就是:异步编程的最高境界,就是根本不用关心它是不是异步

参考文献

async 函数的含义和用法

(译) 6个Async/Await优于Promise的方面

PS:欢迎关注我的公众号 “超哥前端小栈”,交流更多的想法与技术。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100306.html

相关文章

  • 「今日头条」前端面试题和思路解析

    摘要:一篇文章和一道面试题最近,有篇名为张图帮你一步步看清和的执行顺序的文章引起了我的关注。作者用一道年今日头条的前端面试题为引子,分步讲解了最终结果的执行原因。从字面意思理解,让我们等等。当前的最新版本,在这里的执行顺序上,的确存在有问题。 一篇文章和一道面试题 最近,有篇名为 《8张图帮你一步步看清 async/await 和 promise 的执行顺序》 的文章引起了我的关注。 作者用...

    宠来也 评论0 收藏0
  • Javascript中异步编程

    摘要:接下来,我们一起来看看中的异步编程,具体有哪几种。实现异步编程的方法一回调函数上面不止一次提到了回调函数。它是异步编程中,最基本的方法。四对象接下来,我们聊聊与相关的异步编程方法,对象。 showImg(https://segmentfault.com/img/bVbneWy?w=1600&h=1200); 前言 最近,小伙伴S 问了我一段代码: const funB = (value...

    wemall 评论0 收藏0
  • 理解 async/await

    摘要:而函数的命令后面则可以是或者原始类型的值,,,但这时等同于同步操作返回值是。抛出的错误而会被方法回调函数接收到。 ES7 提出的async 函数,终于让 JavaScript 对于异步操作有了终极解决方案。No more callback hell。async 函数是 Generator 函数的语法糖。使用 关键字 async 来表示,在函数内部使用 await 来表示异步。想较于 G...

    kid143 评论0 收藏0
  • 细说JS异步发展历程

    摘要:换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。参考文章珠峰架构课墙裂推荐细说异步函数发展历程异步编程谢谢各位小伙伴愿意花费宝贵的时间阅读本文,如果本文给了您一点帮助或者是启发,请不要吝啬你的赞和,您的肯定是我前进的最大动力。知其然知其所以然,首先了解三个概念: 1.什么是同步? 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了...

    Alfred 评论0 收藏0
  • 细说JS异步发展历程

    摘要:参考文章珠峰架构课墙裂推荐细说异步函数发展历程异步编程谢谢各位小伙伴愿意花费宝贵的时间阅读本文,如果本文给了您一点帮助或者是启发,请不要吝啬你的赞和,您的肯定是我前进的最大动力。 知其然知其所以然,首先了解三个概念: 1.什么是同步? 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用的结果。此调...

    RiverLi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<