【问题标题】:How do I conditionally run a .then() function in a .then .catch chain?如何有条件地在 .then .catch 链中运行 .then() 函数?
【发布时间】:2018-02-27 23:47:36
【问题描述】:

我的代码是这样的:

router.post("/", (req, res, next) => {
foo()
.then((result) => {
    res.send(result)
})
.catch((error) => {
    cosnole.log(error)
})
//I only want the bar() function and everything below it to run if the first promise is rejected and the first .catch function ran
bar()
.then((data) => {
    res.send(data)
})
.catch((error) => {
    console.log(error)
})

})

我只想在第一个 promise 被拒绝并且 .catch 函数触发时才运行 bar() 函数和 .then .catch 函数。

我试过了:

router.post("/", (req, res, next) => {
rejected = false
foo()
.then((result) => {
    res.send(result)
})
.catch((error) => {
    rejected = true
    console.log(error)
})
if(rejected == true)
    bar()
    .then((data) => {
        res.send(data)
    })
    .catch((error) => {
        console.log(error)
    })

})

但是当第一个 foo() 函数的错误被捕获并且 promise 被拒绝时,bar() 函数永远不会被执行。

【问题讨论】:

  • 你的 if 代码在 then() 得到解决之前已经执行

标签: javascript asynchronous ecmascript-6 promise es6-promise


【解决方案1】:

将调用移动到内部 catch。

router.post("/", (req, res, next) => {
    foo()
    .then((result) => {
        res.send(result)
    })
    .catch((error) => {
        cosnole.log(error);
        return bar()
        .then((data) => {
            res.send(data)
        })
        .catch((error) => {
            console.log(error)
        });
    });
});

【讨论】:

  • Promise 不是应该解决嵌套回调问题吗?
  • @doodlemeister 结构化编程需要嵌套控制流。你仍然可以通过简单地返回that's the magic of promises来链接它。
  • @Bergi:即使没有承诺,也不需要嵌套函数。嵌套/闭包只是管理数据集合的一种懒惰方式。我不相信在没有进一步嵌套的情况下解决这个问题不会超过几秒钟。不需要“魔法”。
  • @doodlemeister 对于闭包,我们总是需要至少一层嵌套,但是是的,我们可以提取命名函数并显式管理我们的作用域环境。将它们内联编写更简单,而且通常更具可读性。
  • @Bergi:闭包并不是真正需要的,但是对于封装仅与当前操作相关的函数,一个级别确实很有用。如果使用匿名回调实际上更干净,我们会在所有事情上都使用它们。相反,我们(至少在我看到的大多数代码中)创建命名函数来组织我们的代码。我不明白为什么异步代码应该有任何不同......但这只是我。 ;-)
【解决方案2】:

正如其他人提到的,您只需将代码移动到 catch 处理程序中。

但是,您可能希望将代码简化并更正为

router.post("/", (req, res, next) => {
    foo().catch(bar).then(result => {
        res.send(result);
    , error => {
        console.error(error);
        res.sendStatus(500); // or next(error) or whatever
    });
})

如果您想记录来自foo 的错误,即使它们由bar 处理,您可能需要

foo().catch(error => {
    console.log(error, "(handled by bar)");
    return bar();
}).…

【讨论】:

  • 我从没想过要反转 catch/then(更不用说删除重复的代码)。那更干净/更具可读性。 (+1)
  • 是的,代码在语义上不再与您的严格等价,但重复的 .then(res.send) 只是请求在链的末端共享
  • 我会像这样格式化我的代码,但是 bar() 函数发送的有效载荷与 res.send() 中的 foo() 函数不同
  • @SirSudo 您所说的“不同名称”是什么意思?您应该始终能够将其转换为通用格式。
【解决方案3】:

I/O 调用是异步发生的,因此您的 if 代码会在 res.send() 返回任何响应之前运行。有几种方法可以处理这种情况,这是另一种使用asyn/await 来编写看起来像同步的代码的方法:

router.post("/", async (req, res, next) => {
rejected = false
try {
  await foo()
    .then((result) => {
      res.send(result)
    })
} catch {
  rejected = true
  console.log(error)
}
if(rejected == true)
  bar()
  .then((data) => {
    res.send(data)
  })
  .catch((error) => {
    console.log(error)
  })
})

【讨论】:

  • 不寒而栗。即使在这里,代码也应该被移动到 catch 块内。当您使用async/await 时,您应该在任何地方使用它 - 并且没有单个thencatch 回调离开
  • 我没听懂。我确实同意获得相同的模式并且不会混淆,但是为什么 if 里面首先 catch
  • 我建议不要使用if。可以直接表达就不需要使用控制流标志了。
【解决方案4】:

鉴于代码的异步特性,if (rejected==true) 将始终为 false,因为该代码在第一个 thencatch 之前执行

你可以:

将所有 bar()[...] 代码移入 .catch()

foo()
.then((result) => {
    res.send(result)
})
.catch((error) => {
    console.log(error)
    bar()
    .then((data) => {
        res.send(data)
    })
    .catch((error) => {
        console.log(error)
    })
})

将所有条件执行移到 .catch() 之后的 .then() 中

rejected = false
foo()
.then((result) => {
    res.send(result)
})
.catch((error) => {
    console.log(error)
    rejected = true
})
.then(() => {
  if(rejected) {
    bar()
    .then((data) => {
        res.send(data)
    })
    .catch((error) => {
        console.log(error)
    })
  }
})

【讨论】:

    【解决方案5】:

    尝试返回 bar 以锁住你的承诺。

    router.post("/", (req, res, next) => {
        foo()
        .then((result) => {
            res.send(result)
        })
        .catch((error) => {
            console.log(error);
            return bar;
        })
        .then((data) => {
            res.send(data);
        })
        .catch((error) => {
            console.log(error)
        });
    });
    

    ----------------- 编辑 -----------------

    这是一个 ExpressJS 示例的完全可测试副本,其中包含您的问题:

    const express = require("express");
    const app = express();
    const port = process.env.PORT || 4000;
    
    app.get("/", function(req, res) {
        foo = new Promise(function(resolve, reject) {
            // resolve("resolved"); // Uncomment for resolve
            reject("rejected");
        });
    
        bar = new Promise(function(resolve, reject) {
            resolve("myData");
        });
    
        foo.then((result) => {
            res.send(result);
        }).catch((error) => {
            console.log(error);
            return bar;
        }).then((data) => {
            res.send(data);
        });
    });
    
    app.listen(port, function () {
        console.log("Listening on port: " + port);
    });
    

    【讨论】:

    • 你确定吗?我在这段代码中没有看到任何告诉我第二个.then 只会在第一个catch 被触发时发生。它怎么会知道data 得到了什么?
    • 嗨 Doodle Meister,我提供了一个服务器示例来回答您的问题
    • 我很想知道答案被否决的原因
    • 因为 doodlemeister 是对的。您的代码两次调用res.send,这是一个错误。
    • 没有尝试服务器部分,但你得到log("rejected"); send("myData")(当 foo 拒绝时)或send("resolved"); send(undefined)(当 foo 满足时)。
    猜你喜欢
    • 2020-03-15
    • 1970-01-01
    • 2016-02-14
    • 2021-10-17
    • 2021-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-31
    相关资源
    最近更新 更多