在几乎所有地方使用async/await是否可行?

16

我目前正在编写一个用于个人使用的小型NodeJS CLI工具,并决定尝试使用Babel的ES7 async/await功能。

由于这是一个网络工具,所以我显然有异步网络请求。我为request包编写了一个简单的包装器:

export default function(options) {
    return new Promise(function(resolve, reject) {
        request({...options,
            followAllRedirects: true,
            headers: {
                "user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0"
            }
        }, (error, response, body) => {
            if(error) {
                return reject(error);
            }
            resolve({response: response, body: body});
        });
    });
}

现在我可以做一些像这样的事情

async function getGooglePage() {
    try {
        var r = await request({url: "http://google.com"});

        console.log(r.body);
        console.log("This will be printed in the end.")
    } catch(e) {
        console.log(e);
    }
}
getGooglePage();

现在我有一个问题:我在许多地方进行请求,我必须将所有这些函数标记为async,这是一个好的实践吗?我的意思是我的代码中几乎每个函数都应该是async,因为我需要从其他async函数中等待结果。这就是我认为我误解了异步/等待的概念的原因。

3个回答

17

异步/等待有时被称为“具有感染性”或“病毒性”(至少在C#领域中是这样),因为为了使其有效,它需要被整个调用链支持。强制将异步操作变同步可能会导致意外结果,因此您应该从原始方法一直扩展到使用它的顶级消费者。换句话说,如果您创建或使用一个使用它的类型,则该类型也应该实现它,以此类推一直延伸到整个链条。因此,是的,您应该在每个依赖于异步/等待的函数上添加async修饰符。但是请注意,不要把异步操作预先加到不需要实现或使用它的函数上。

只要想一想:如果您使用async(我指由await调用),那么您就是async。避免将async调用压缩成同步。


2
我认为病毒式传播在JavaScript中并不适用,因为使用Promise返回函数还是异步函数更多是一种实现选择。此外,JavaScript中的类型也非常少。 - jib
@jib 公平地说,像C#世界这样的东西只返回一个通用的Task,在这种情况下,它实际上有点像一个promise,并且async/await只是语法糖。再次强调,我认为混合这两个概念是创造预期结果的好方法,但我理解你的意思,即在C#世界中,编译器要求使用它,这加强了它的传播性,但在JavaScript中没有这样的事情。 - moribvndvs
1
我同意@jib的观点。异步操作并不具有传染性。异步函数与返回Promise的普通函数之间几乎没有区别。异步函数可以像Promise一样处理。 - Maciej Krawczyk

6

我在很多地方都有请求,我必须将所有这些函数标记为异步

是的,如果您的所有代码都是异步的,那么您需要在每个地方使用async函数。

然而,让所有代码都变成异步会使事情变得复杂。您必须随时担心竞争条件,确保正确处理可重入函数,并且要记住,在每个await期间基本上任何事情都可能发生。

我的意思是,我的代码中几乎每个函数都应该是异步的,因为我需要等待其他异步函数的结果。

这可能不是最佳实践。您可以尝试将代码分解为较小的单元,其中大部分通常不是异步的。因此,不要编写以下内容:

async function getXandThenDoY(xargs) {
    let res = await get(xargs);
    …
    return …;
}

你应该考虑编写两个函数。
function doY(res) {
    // synchronousreturn …;
}
function getXandDoY(xargs) {
    // asynchronous
    return get(xargs).then(doY);
}
/* or, if you prefer:
async function getXandDoY(xargs) {
    return doY(await get(xargs));
}
*/

2

我有同样的问题

并在这里找到了一个很好的答案 → “陷阱3:整个堆栈都需要是异步的”

不,async/await不具有传染性。(我也曾有过同样的想法)

您始终可以像处理promise一样处理同步函数的结果,然后您就回到了正常状态。

来自developer.mozilla.org:

async函数声明定义了一个异步函数...

返回值: 一个Promise,它将解析为异步函数返回的值, 或者被拒绝,因为在异步函数内部抛出了未捕获的异常。

示例代码:

const log = console.log; // just lazy shorthand

// just to delay, as seen in many places
function promiseTimeout(time, value) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() { resolve(value); }, time);
    });
};

// the thing you care about
async function foo() {
    Promise.resolve('here')
    .then((a) => {log('doing stuff '+a); return 'pear'})
    .then(function(v) {
        return promiseTimeout(1000,v)
    });
};

// treat async-function like promise:
foo().then(function(){ log('folling up in main, ')});
// log('bad end');

让你获得:

doing stuff here
following up in main

启用“bad end”会过早显示。只有使用await才能等待东西。(如果你这样做,请记住:它只是语法糖,可以避免将后续代码塞入.then()子句中...很好,但也仅此而已。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接