Javascript 如何兑现这些承诺? 背景

Javascript 如何兑现这些承诺? 背景,javascript,functional-programming,promise,es6-promise,Javascript,Functional Programming,Promise,Es6 Promise,我有一个向服务器发出请求的函数。如果请求失败,我想: 1.记录错误 2.运行终端命令 2.1记录命令是否失败或成功 为了实现这一点,我有以下代码: const createRequest = ( { request, logger, terminal } ) => ( { endpoint, timeout } ) => request.get( endpoint, { timeout } ) .then( response =>

我有一个向服务器发出请求的函数。如果请求失败,我想: 1.记录错误 2.运行终端命令 2.1记录命令是否失败或成功

为了实现这一点,我有以下代码:

const createRequest = ( { request, logger, terminal } ) => ( { endpoint, timeout } ) =>
    request.get( endpoint, { timeout } )
        .then( response =>
            logger.info( { event: "Heartbeat request succeeded.", status: response.status } )
        )
        .catch( err =>
            logger.error( { event: "Heartbeat request failed.", err } )
                .then( ( ) => terminal.runAsync( "pm2 restart myAPI" ) )
                .then( ( ) => logger.info( { event: "Restarted API." } ) )
                .catch( err => logger.error( { event: "Failed to restart API.",  err } ) )
        );
现在,有几件事需要注意: -日志记录是异步的(将信息发送到远程服务器) -运行终端命令是异步的 -发出请求(显然)是异步的

问题 我的问题是,我的捕获物里面有一个承诺,这意味着我有巢穴。 现在,我强烈反对承诺的嵌套,所以我真的很想摆脱它,但我不知道怎么做

问题:
  • 有没有可能摆脱
    catch
    中的嵌套承诺
  • 如果是,怎么做

  • 我认为有两种方法

    选择1

    继续使用Promise,但代码需要一些更改:

    const createRequest = ({ request, logger, terminal }) => ({ endpoint, timeout }) =>
      request.get(endpoint, { timeout })
        .then(response =>
          logger.info({ event: "Heartbeat request succeeded.", status: response.status })
        )
        .catch(err => {
          // after a catch the chain is restored
          return logger.error({ event: "Heartbeat request failed.", err })
        })
        .then(() => terminal.runAsync("pm2 restart myAPI"))
        .then(() => logger.info({ event: "Restarted API." }))
        .catch(err => logger.error({ event: "Failed to restart API.", err }))
    
    选择2

    使用wait/async,将async更改为sync

    问题?

    我的问题是,我的捕获物里面有一个承诺,这意味着我有巢穴。现在,我强烈反对承诺的嵌套,所以我真的很想摆脱它,但我不知道怎么做

    -凤凰城火焰酒店

    问题是你认为你有问题——或者你把这个问题贴在StackOverflow上而不是其他网站上。显示了您对嵌套承诺的天真看法

    你会得到一大堆相互嵌套的承诺:

    loadSomething().then(function(something) {
        loadAnotherthing().then(function(another) {
                        DoSomethingOnThem(something, another);
        });
    });
    
    之所以这样做是因为您需要对这两个承诺的结果进行处理,因此无法链接它们,因为then()只传递上一次返回的结果

    您这样做的真正原因是因为您不知道
    Promise.all()
    方法

    -代码猴子

    不,
    承诺。所有
    有时只能替换嵌套的承诺。一个简单的反例——在这里,一个承诺的价值取决于另一个,因此这两个承诺必须排序

    getAuthorByUsername (username) .then (a => getArticlesByAuthorId (a.id))
    
    嵌套承诺并不总是必要的,但国际海事组织称之为“反模式”,并鼓励人们在意识到差异有害之前避免嵌套


    语句不起作用

    你可能再次被误导的节目

    别误会,async/await不是世界上所有邪恶的源头。事实上,在使用了几个月后,我就学会了喜欢它。因此,如果您觉得编写命令式代码很方便,那么学习如何使用async/await来管理异步操作可能是一个不错的选择

    但是,如果您喜欢承诺,并且希望学习并将越来越多的函数式编程原则应用到代码中,那么您可能希望完全跳过异步/等待代码,停止思考命令式,转而使用这种新的旧范式

    -加布里埃尔·蒙特斯

    只是这没有任何意义。如果查看JavaScript中的所有命令式关键字,您会发现它们的计算结果都不是一个值。为了说明我的意思,请考虑

    let total = if (taxIncluded) { total } else { total + (total * tax) }
    // SyntaxError: expected expression, got keyword 'if'
    

    或者如果在另一个表达式

    的中间尝试使用<代码> < <代码> >
    makeUser (if (person.name.length === 0) { "anonymous" } else { person.name })
    // SyntaxError: expected expression, got keyword 'if'
    
    items .concat (await getMoreItems ()) // [ ... ]
    
    这是因为
    如果
    是一个语句,它的计算结果永远不会是一个值——相反,它只能依赖于副作用

    if (person.name.length === 0)
      makeUser ("anonymous") // <-- side effect
    else
      makeUser (person.name) // <-- side effect
    
    对于
    do
    while
    switch
    、甚至
    return
    和所有其他强制关键字也是如此–它们都是语句,因此依赖副作用来计算值

    那么,什么计算为值?表达式计算为一个值

    1                          // => 1
    5 + 5                      // => 10
    person.name                // => "bobby"
    person.name + person.name  // => "bobbybobby"
    toUpper (person.name)      // => "BOBBY"
    people .map (p => p.name)  // => [ "bobby", "alice" ]
    

    异步
    等待
    不是语句

    可以将异步函数分配给变量

    const f = async x => ...
    
    const items = await getItems () // [ ... ]
    
    或者可以将Asynchronous函数作为参数传递

    someFunc (async x => ... )
    
    即使
    async
    函数不返回任何结果,
    async
    仍然保证我们将收到承诺值

    const f = async () => {}
    f () .then (() => console.log ("done"))
    // "done"
    
    您可以等待一个值并将其分配给变量

    const f = async x => ...
    
    const items = await getItems () // [ ... ]
    
    或者您可以
    等待另一个表达式中的值

    makeUser (if (person.name.length === 0) { "anonymous" } else { person.name })
    // SyntaxError: expected expression, got keyword 'if'
    
    items .concat (await getMoreItems ()) // [ ... ]
    
    这是因为
    async
    /
    wait
    表单表达式可以与函数式一起使用。如果您试图学习函数式风格并避免
    async
    wait
    ,那只是因为您被误导了。如果
    async
    await
    只是命令式风格,那么这样的事情就永远不可能发生

    const asyncUnfold = async (f, initState) =>
      f ( async (value, nextState) => [ value, ...await asyncUnfold (f, nextState) ]
        , async () => []
        , initState
        )
    

    实例

    这里是一个实际的例子,我们有一个记录数据库,我们希望执行递归查找,或者其他什么

    const data =
      { 0 : [ 1, 2, 3 ]
      , 1 : [ 11, 12, 13 ]
      , 2 : [ 21, 22, 23 ]
      , 3 : [ 31, 32, 33 ]
      , 11 : [ 111, 112, 113 ]
      , 33 : [ 333 ]
      , 333 : [ 3333 ]
      }
    
    一个异步函数
    Db.getChildren
    位于您和数据之间。如何查询节点及其所有子节点

    const Empty =
      Symbol ()
    
    const traverse = (id) =>
      asyncUnfold
        ( async (next, done, [ id = Empty, ...rest ]) =>
            id === Empty
              ? done ()
              : next (id, [ ...await Db.getChildren (id), ...rest ])
        , [ id ]
        )
    
    traverse (0)
    // => Promise [ 0, 1, 11, 111, 112, 113, 12, 13, 2, 21, 22, 23, 3, 31, 32, 33, 333, 3333 ]
    
    用蒙特斯的话来说,这是一个来自“JavaScript开发者天堂”的纯程序。它是使用函数表达式编写的,错误会相应地冒出来,我们甚至不必触摸
    。然后

    我们可以使用命令式风格编写相同的程序。或者我们可以使用
    编写它的函数式。然后也可以使用
    编写它。我们可以用各种方式编写它,我想这就是重点所在–由于
    async
    wait
    能够形成表达式,我们可以在各种样式中使用它们,包括函数样式

    在下面的浏览器中运行整个程序

    const asyncUnfold=async(f,init)=>
    f(异步(x,acc)=>[x,…等待异步展开(f,acc)]
    ,async()=>[]
    ,init
    )
    常数分贝=
    {getChildren:(id)=>
    新承诺(r=>setTimeout(r,100,数据[id]| |[]))
    }
    常数为空=
    符号()
    常量遍历=(id)=>
    异步展开
    (异步(下一步,完成,[id=Empty,…rest])=>
    id==空
    ?完成()
    :next(id,[…wait Db.getChildren(id),…rest])
    ,[id]
    )
    常量数据=
    { 0 : [ 1, 2, 3 ]
    , 1 : [ 11, 12, 13 ]
    , 2 : [ 21, 22, 23 ]
    , 3 : [ 31, 32, 33