如何不忘记在 Javascript 中到处使用 await?

Posted

技术标签:

【中文标题】如何不忘记在 Javascript 中到处使用 await?【英文标题】:How not to forget using await everywhere in Javascript? 【发布时间】:2018-01-08 22:14:27 【问题描述】:

尝试写一个小chrome扩展,依赖chrome.*接口的大量回调查询函数,我很快就进入了promises和async/await,因为我需要保证某些操作的顺序,同时尝试避免callback hell。

但是,一旦我将 async/await 引入到某些函数中,每个使用它们的函数也必须转换为 async function 以便能够 await 返回值。最终,甚至一些全局常量也变成了 Promise,例如

const DEBUG = new Promise(function(resolve)
    chrome.management.getSelf(resolve);
).then(function(self)
    return self.installType == 'development';
);

但是,现在我需要到处写 await 并引入奇怪的错误,例如 if(DEBUG)... 总是被执行变得太容易了。

虽然看起来 possible to identify the errors using ESLINT,但到处写 await 似乎是不必要的麻烦,因此我想知道 Javascript 是否有一些我缺少的更好的构造?

(主观上我当前对 await/async 的使用似乎倒退了;除非明确等待,否则 Promise 会保持原样,但我似乎更希望在 async 函数中默认等待 Promise 并仅在明确请求时才保留为裸 Promise .)

【问题讨论】:

不确定这是否是您要查找的内容,但您始终可以链接您的 Promise.prototype.then() 语句,即 new Promise(...).then(...).then(...) 关于全局常量,只需将整个模块包装在一个函数中,该函数由 awaits 和 Promise.all 与所有异步加载的常量一起执行,然后才执行您的主脚本。 "一旦我介绍了 async/await" - 我很确定您之前遇到过完全相同的问题,因为您的逻辑一直是异步的。不管你是使用回调、承诺还是带有await 糖的承诺。 @PatrickBarr 这本质上是具有相同问题的不同语法。现在只有这样,而不是const x = await getX(); const y = await x.getY(); dostuff(x,y); ...,我会写getX().then(x => Promise.all([x, x.getY()])).then(([x,y]) => dostuff(x,y); ...),这并不难忘记做,但如果忘记了更难重构。 Great article on how to avoid, that everything up to the top has to be async/await once you start the habbit。请参阅“陷阱 3”(剧透:您可以将异步函数的结果视为承诺) 【参考方案1】:

由于缺少可以轻松捕获此类错误的类型系统(您考虑过 Typescript 还是 Flow?),您可以使用 Systems Hungarian Notation 作为变量名。选择像PPromise$ 这样的后缀前缀并将其添加到所有的promise 变量中,类似于通常使用Async 后缀命名的异步函数。然后只做类似的事情

const debug = await debugPromise

您可以快速看到if (debug) 很好,但if (debugPromise) 不是。


一旦我在某些函数中引入了 async/await,每个使用它们的函数也必须转换为 async 函数,以便能够等待返回值。最终,甚至一些全局常量也变成了 Promise

我不会那样做。尝试使尽可能少的函数异步。如果他们本身并没有做本质上异步的事情,而只是依赖于一些 Promise 的结果,那么将这些结果声明为函数的参数。一个简单的例子:

// Bad
async function fetchAndParse(options) 
    const response = await fetch(options);
    // do something
    return result;

// usage:
await fetchAndParse(options)

// Good:
function parse(response) 
    // do something
    return result;

// usage:
await fetch(options).then(parse) // or
parse(await fetch(options))

同样的模式也可以应用于全局变量——要么让它们成为每个函数的显式参数,要么让它们成为包含所有其他函数作为闭包的模块函数的参数。然后await 全局承诺只在模块中一次,在声明或执行其他任何内容之前,然后使用纯结果值。

// Bad:
async function log(line) 
    if (await debugPromise)
        console.log(line);

async function parse(response) 
    await log("parsing")
    // do something
    return result;

… await parse(…) …

// Good:
(async function mymodule() 
    const debug = await debugPromise;
    function log(line) 
        if (debug)
            console.log(line);
    
    function parse(response) 
        log("parsing")
        // do something
        return result;
    
    … parse(…) …
());

【讨论】:

关于命名,Async 后缀很常见;这是.NET convention,也是adopted by Bluebird。 @StephenCleary 谢谢,我已经在想是否应该澄清一下,对于 promise 变量和异步函数不会使用 same 后缀。 谢谢伯吉。解释得很清楚。这很好。还有两种情况 - 1)在异步/等待承诺使用期间出现错误。 2) 拒绝使用 async/await for promises 时的使用问题。您如何遵循处理异步/等待中的错误的良好做法?其次,在执行 async/await for promises 时有什么好的方法来处理拒绝代码?任何参考或代码示例都可以 @Gary 就像在其他地方一样使用try/catch。或some variations,具体取决于您的具体需求。 @FrankNocke“可以被视为”几乎没有意义。它们承诺:-)

以上是关于如何不忘记在 Javascript 中到处使用 await?的主要内容,如果未能解决你的问题,请参考以下文章

如何不忘记再次将量角器转换为角度同步?

javascript介绍及如何在html中使用js与jQuery

如何在没有 eval 的 javascript 中编写计算器

如何学习才能不容易忘记

在用户的时区显示当前时间,不要忘记夏令时

rsync工具报错