如何解除这些 Promise 的嵌套? [英] How to unnest these Promises?

查看:17
本文介绍了如何解除这些 Promise 的嵌套?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a function that makes a request to a server. If the request fails, I want to: 1. log the error 2. run a terminal command 2.1 log if the command failed or succeeded

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

To achieve this I have the following code:

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 } ) )
        );

现在,有几点需要注意:- 日志是异步的(将信息发送到远程服务器)- 运行终端命令是异步的- 发出请求(显然)是异步的

Now, there are a few things to notice: - logging is async ( sends info to a remote server ) - running the terminal command is async - making the request is ( obviously ) async

我的问题是我的 catch 里面有一个 Promise,这意味着我有嵌套.现在,我强烈反对嵌套承诺,所以我真的很想摆脱它,但我只是不知道如何.

The problem I have is that my catch has a Promise inside, which means I have nesting. Now, I am strongly against nesting of promises, so I really want to get rid of that, but I just don't see how.

  1. 是否有可能摆脱 catch 中的嵌套承诺?
  2. 如果是,如何?

推荐答案

问题?

我的问题是我的 catch 里面有一个 Promise,这意味着我有嵌套.现在,我强烈反对嵌套承诺,所以我真的很想摆脱它,但我只是不知道如何.

The problem I have is that my catch has a Promise inside, which means I have nesting. Now, I am strongly against nesting of promises, so I really want to get rid of that, but I just don't see how.

– Flame_Phoenix

– Flame_Phoenix

问题在于您认为自己有问题——或者您可能将此问题发布到 StackOverflow 而不是 CodeReview.您链接的文章 展示了您对嵌套承诺的幼稚看法

The problem is that you think you have a problem – or maybe that you posted this question on StackOverflow instead of CodeReview. The article you linked shows where you adopt a naive view about nested promises

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

You get a whole bundle of promises nested in eachother:

loadSomething().then(function(something) {
    loadAnotherthing().then(function(another) {
                    DoSomethingOnThem(something, another);
    });
});

你这样做的原因是因为你需要对两个 promise 的结果做一些事情,所以你不能链接它们,因为 then() 只传递了前一个 return 的结果.

The reason you’ve done this is because you need to do something with the results of both promises, so you can’t chain them since the then() is only passed the result of the previous return.

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

The real reason you’ve done this is because you don’t know about the Promise.all() method.

——代码猴,http://taoofcode.net

不,Promise.all 只能有时替换嵌套的 Promise.一个简单的反例——这里,一个 promise 的值取决于另一个,因此两个 必须 被排序

No, Promise.all can only sometimes replace nested promises. A simple counter-example – here, one promise's value depends on the the other and so the two must be sequenced

getAuthorByUsername (username) .then (a => getArticlesByAuthorId (a.id))

语句不起作用

其他链接文章 显示您可能再次被误导的地方

The other linked article shows where you may have been misguided again

不要误会我的意思,async/await 并不是世界上所有邪恶的根源.在使用它几个月后,我实际上学会了喜欢它.因此,如果您觉得编写命令式代码很舒服,那么学习如何使用 async/await 来管理异步操作可能是一个不错的举措.

Don’t get me wrong, async/await is not the source of all evil in the world. I actually learned to like it after a few months of using it. So, if you feel confortable writing imperative code, learning how to use async/await to manage your asynchronous operations might be a good move.

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

But if you like promises and you like to learn and apply more and more functional programming principles to your code, you might want to just skip async/await code entirely, stop thinking imperative and move to this new-old paradigm.

——加布里埃尔·蒙特斯

– Gabriel Montes

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

Only this doesn't make any sense. If you look at all of the imperative keywords in JavaScript, you'll notice none of them evaluate to a value. To illustrate what I mean, consider

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

或者如果我们尝试在另一个表达式中间使用 if

Or if we try to use if in the middle of another expression

makeUser (if (person.name.length === 0) { "anonymous" } else { person.name })
// SyntaxError: expected expression, got keyword 'if'

那是因为if是一个语句,它永远不会计算为一个值——相反,它只能依赖副作用.

That's because if is a statement and it never evaluates to a value – instead, it can only rely on side effects.

if (person.name.length === 0)
  makeUser ("anonymous") // <-- side effect
else
  makeUser (person.name) // <-- side effect

for 之下永远不会评估为值.相反,它依靠副作用来计算 sum

Below for never evaluates to a value. Instead it relies on side effects to compute sum

let sum = 0
let numbers = [ 1, 2, 3 ]
for (let n of numbers)
  sum = sum + n        // <-- side effect
console.log (sum)      // 6

同样适用于dowhileswitch,甚至return 和所有其他命令式关键字——它们都是语句,因此依赖副作用来计算值.

The same is true for do, while, switch, even return and all of the other imperative keywords – they're all statements and therefore rely upon side effects to compute values.

那么什么是值呢?表达式求值

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

<小时>

asyncawait 不是语句


async and await are not statements

你可以给一个变量赋值一个异步函数

You can assign an asynchronous function to a variable

const f = async x => ...

或者你可以传递一个异步函数作为参数

Or you can pass an asyncrhonous function as an argument

someFunc (async x => ... )

即使 async 函数什么都不返回,async 仍然保证我们会收到一个 Promise 值

Even if an async function returns nothing, async still guarantees we will receive a Promise value

const f = async () => {}
f () .then (() => console.log ("done"))
// "done"

你可以await一个值并将它赋给一个变量

You can await a value and assign it to a variable

const items = await getItems () // [ ... ]

或者你可以await另一个表达式中的值

Or you can await a value in another expression

items .concat (await getMoreItems ()) // [ ... ]

因为async/await 形成表达式,它们可以与函数式风格一起使用.如果您正在尝试学习函数式风格并避免 asyncawait,那只是因为您被误导了.如果 asyncawait 只是命令式风格,这样的事情永远不可能

It's because async/await form expressions that they can be used with functional style. If you are trying to learn functional style and avoid async and await, it is only because you've been misguided. If async and await were imperative style only, things like this would never be possible

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

<小时>

真实例子

这是一个实际示例,我们有一个记录数据库,我们希望执行递归查找或其他操作...

Here's a practical example where we have a database of records and we wish to perform a recursive look-up, or something...

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 站在你和你的数据之间.你如何查询一个节点及其所有的后代?

An asynchronous function Db.getChildren stands between you and your data. How do you query a node and all of its descendants?

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 开发者的天堂"发送的纯程序,用 Montes 的话来说.它是使用函数表达式编写的,错误会相应地冒出来,我们甚至不必触摸 .then.

A pure program sent from the "heaven of JavaScript developers", to put it in the words of Montes. It's written using a functional expression, errors bubble up accordingly, and we didn't even have to touch .then.

我们可以使用命令式风格编写相同的程序.或者我们也可以使用 .then 编写函数式风格.我们可以用各种方式编写它,我想这就是重点——感谢 asyncawait 形成表达式的能力,我们可以以各种风格使用它们,包括功能风格.

We could write the same program using imperative style. Or we could write it functional style using .then too. We can write it all sorts of ways and I guess that's the point – Thanks to async and await's ability to form expressions, we can use them in a variety of styles, including functional style.

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

Run the entire program in your browser below

const asyncUnfold = async (f, init) =>
  f ( async (x, acc) => [ x, ...await asyncUnfold (f, acc) ]
    , async () => []
    , init
    )

const Db =
  { getChildren : (id) =>
      new Promise (r => setTimeout (r, 100, data [id] || []))
  }

const Empty =
  Symbol ()

const traverse = (id) =>
  asyncUnfold
    ( async (next, done, [ id = Empty, ...rest ]) =>
        id === Empty
          ? done ()
          : next (id, [ ...await Db.getChildren (id), ...rest ])
    , [ id ]
    )
    
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 ]
  }

traverse (0) .then (console.log, console.error)
// => Promise
// ~2 seconds later
// [ 0, 1, 11, 111, 112, 113, 12, 13, 2, 21, 22, 23, 3, 31, 32, 33, 333, 3333 ]

这篇关于如何解除这些 Promise 的嵌套?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆