几乎在任何地方都可以使用 async/await 吗?

Posted

技术标签:

【中文标题】几乎在任何地方都可以使用 async/await 吗?【英文标题】:Is it OK to use async/await almost everywhere? 【发布时间】:2016-05-24 15:20:34 【问题描述】:

我目前正在编写供个人使用的小型 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,因为我需要await 来自其他async 函数的结果。这就是为什么我认为我误解了 async/await 概念。

【问题讨论】:

【参考方案1】:

我有the same question。

发现 a great answer here → ‘陷阱 3:你的整个堆栈需要异步’

不,async/await 不会传染。(我也相信了一段时间)

您始终可以将同步函数的结果视为承诺,然后您就会恢复正常。

来自 developer.mozilla.org:

异步函数声明定义了一个异步函数……

返回值: 一个 Promise 将用 value 解决 由 async 函数返回,或因未捕获的异常而被拒绝 从异步函数中抛出。

示例代码:

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() 子句中……很好,但仅此而已。)

【讨论】:

【参考方案2】:

async/await 有时被称为“传染性”或“病毒性”(或者在 C# 世界中是这样的),因为为了使其有效,它需要在调用链的整个过程中得到支持。强制异步执行同步可能会导致意想不到的结果,因此您应该将其从原始方法一直扩展到使用它的***使用者。换句话说,如果您创建或使用一个使用它的类型,该类型也应该实现它,依此类推。所以是的,预计您将 async 添加到本身依赖它的每个函数中。但是请注意,您不应将抢先添加异步添加到实际上并没有实现或不需要它的函数中。

试想一下:如果你使用async(我的意思是await),你就是async。避免将 async 调用压缩成同步的东西。

【讨论】:

我不认为病毒式的部分适用于 javascript,因为返回 promise 的函数是用 promise 实现还是作为异步函数实现,这似乎更像是一种实现选择。 JavaScript 中的类型也很少。 @jib 平心而论,像 C# 世界之类的东西只是返回一个通用的任务,在这种情况下它的使用有点像一个承诺,而async/await 只是语法糖。再次,我认为混合这两个概念是创建预期结果的好方法,但我理解你的意思,在 C# 世界中,病毒性质通过编译器需要它的事实得到加强,但没有这样的JavaScript 中的东西。 我同意@jib 异步没有什么病毒式的。异步函数与返回承诺的普通函数之间几乎没有区别。可以像 Promise 一样处理异步函数。【参考方案3】:

我在很多地方都做了请求,我必须将所有这些功能标记为异步

是的,如果您的所有代码都是异步的,那么您将在任何地方使用 async 函数。

虽然你的所有代码都是异步的,但这会让事情变得复杂。您必须担心任何地方的竞争条件,确保正确处理可重入函数,并记住在每个await 期间,基本上任何事情都可能发生。

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

这可能不是最佳做法。您可以尝试将代码分解为更小的单元,其中大多数通常不是异步的。所以不要写

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

你应该考虑做两个函数

function doY(res) 
    // synchronous
    …
    return …;

function getXandDoY(xargs) 
    // asynchronous
    return get(xargs).then(doY);

/* or, if you prefer:
async function getXandDoY(xargs) 
    return doY(await get(xargs));

*/

【讨论】:

以上是关于几乎在任何地方都可以使用 async/await 吗?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在任何地方替换全局“operator new()”?

django 在任何地方都提供相同的会话

async await yield

大结果在任何地方都很慢,但在本地

Mac OSX 中的文本大小比使用 Qt 的 Windows 小。我想在任何地方都有相同的尺寸

检查在任何地方的检查应用程序中都提出问题