滥用 await as return 语句的缺点?

Downside to abusing await as return statement?

我在 Javascript 中偶然发现了一种有趣但相当黑的方法来编写带有 promises 的线性代码,如下所示:

const return_by_death = new Promise((resolve) => {});

const signup = async (req, res) => {
    const user = req.body.username;
    const pass = req.body.password;

    if(!user || !pass) {
        res.sendStatus(400);
        return;
    }

    const hash_data = await generate_hash(pass).catch(async (err) => {
        res.sendStatus(500);
        await return_by_death;
    });

    const new_account = new models.account.model({
        username: user,
        salt: hash_data.salt,
        hash: hash_data.hash
    });
    // ...
};

从我的实验来看,它似乎以这样的方式工作:如果来自 generate_hash 的承诺被拒绝,它将进入我的错误处理程序而不移动到 new_account 行。我的问题如下:

  1. 这是否通过生成无限期挂起的承诺或执行线程来浪费内存?

  2. expressjs 是否保留传递给 app.get(path, func) 的函数,它会跟踪可能永远无法解决的注册承诺?

  3. 有更好的方法吗?

编辑:基于来自@CertainPerformance 的answer/information,我提出了以下解决方案

class PromiseRunError {
    constructor(obj) {
        this.obj = obj;
    }
}

Promise.prototype.run = function() {
    return this.catch((err) => {return new PromiseRunError(err)}).then((data) => {
        if(data instanceof PromiseRunError) {
            return [undefined, data.obj];
        }
        return [data, undefined];
    });
};

const [hash_data, hash_gen_err] = await generate_hash(pass).run();

if(hash_gen_err) {
    res.sendStatus(500);
    return;
}

是的,在这里使用永久未解决的 Promise 是个问题。

  • 如果signup被多次调用,并且产生了很多挂起的Promise,随着时间的推移会使用越来越多的内存;每次调用 signup 都会产生一个新的闭包,其中分配了 userpassreqres 变量,直到函数结束。 (但如果有错误,它永远不会结束。)
  • 从可读性的角度来看,它非常令人困惑。

如果你想让事物尽可能平坦,而不是将所有事物包围在 try/catch 中,假设 generate_hash return 是一个 Promise,如果它解析,将解析为真实的东西,只是不要 return .catch 处理程序中的任何内容,然后在继续之前检查 hash_data 是否真实:

const signup = async (req, res) => {
    const user = req.body.username;
    const pass = req.body.password;

    if(!user || !pass) {
        res.sendStatus(400);
        return;
    }

    const hash_data = await generate_hash(pass).catch(() => {});
    if (!hash_data) {
        res.sendStatus(500);
        return;
    }

    const new_account = new models.account.model({
        username: user,
        salt: hash_data.salt,
        hash: hash_data.hash
    });
    // ...
};

我更喜欢 try/catch,不过:

const signup = async (req, res) => {
    try {
        const { user, pass } = req.body;
        if (!user || !pass) {
            res.sendStatus(400);
            return;
        }

        const hash_data = await generate_hash(pass)
        const new_account = new models.account.model({
            username: user,
            salt: hash_data.salt,
            hash: hash_data.hash
        });
        // ...
    } catch (e) {
        res.sendStatus(500);
        return;
    }
};