为什么在 catch 块内的代码完成之前评估 then 块内的代码

Why is code inside then block being evaluated before the code inside catch block completes

代码如下:

function f1(id) {
    return new Promise((resolve, reject) => {
        if (id === 123132) {
            resolve([{id:1,name:"John"}, {id:10, name:"Chris"}])

        } else {
            reject(new Error("f1 fails - can't find info for id"))
        }


    })

}

function f2(gender) {
    return new Promise((resolve, reject) => {
        if (gender === "FEMALE") {
            resolve([{id:6, name:"Stacy"}, {id:1, name:"John"}, {id:13, name:"Veronica"}])
        } else {
            reject(new Error("f2 Fails"))
        }
    })

}



 function Test(User, N){
    return new Promise((resolve, reject)=>{
        f1(User.id).catch(err=>{

            console.log(err.message)
            //this api returns an error, so call the other one
            f2(User.gender).catch(err=>{
                console.log(err.message)
                reject(new Error("Both api calls have failed"))
            }).then(res=>{
                if (res.length<N){
                  reject(new Error("Not Enough Data..."))
                } else {
                    console.log("We are here..")
                    console.log(res)
                    resolve(res.slice(0,N).map(item=>item.name))
                }
            })
        })
        .then(res1=>{
            console.log("We should not be here but we are...")
            console.log(res1)
          if (res1.length<N){
              f2(User.gender).catch(err=>{
                    console.log(err.message)
                //   reject(new Error("Not Enough Data"))
                  throw new Error("Not Enough Data")
              }).then(res2=>{
                  res3 = filterDups2(res1, res2)
                  res3.length>=N ? resolve(res3.slice(0,N).map(item=>item.name)) :
                  reject(new Error("Not Enough Data"))
              })
          } else {
              console.log("Why are we not here...")
              resolve(res1.slice(0,N).map(item=>item.name))
          }
        })
    })
}


function filterDups2(list1, list2){
    jointRes = [...list1, ...list2]
    ans = Array.from(new Set(jointRes.map(item=>JSON.stringify(item)))).map(item=>JSON.parse(item))  
    return ans
  }

let user = { id: 123, gender: "FEMALE" }
let result = Test(user, 2)


result.then(res=> console.log(res)).catch(err=> {
    console.log("Inside result...")
    console.log(err.message)
})


这是控制台上的输出:

f1 fails - can't find info for id
We should not be here but we are...
undefined
We are here..
[
  { id: 6, name: 'Stacy' },
  { id: 1, name: 'John' },
  { id: 13, name: 'Veronica' }
]

[ 'Stacy', 'John' ]

(node:2968) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'length' of undefined
    at /Users/daanishraj/Desktop/Front-End-Work/WDB-Feb-2021/JavaScript/Practice/interviews/zalando/forWhosebug.js:50:20
(Use `node --trace-warnings ...` to show where the warning was created)
(node:2968) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:2968) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

我不明白为什么我们在控制台上有以下两个东西

We should not be here but we are...
undefined

出现在这些输出之前:

We are here..
[
  { id: 6, name: 'Stacy' },
  { id: 1, name: 'John' },
  { id: 13, name: 'Veronica' }
]
[ 'Stacy', 'John' ]

我知道因为最外层的 catch 块,即 f1(U.id).catch() 已解析,这将使我们进入最外层的 then 块,即 f1(U.id).then ().但是这些输出的顺序表明我们在解析 catch 块之前进入 then 块。

  1. 为什么会这样?
  2. 既然我们确实进入了 then 块,一旦 res1 被认为是 undefined,为什么控件不移动到我们将输出 Why are we not here... 的 then 块的底部?

总的来说,如果您能阐明这里的执行顺序,那就太好了!

Why is this the case?

因为您没有指示承诺链等待来自 catch 处理程序的异步结果。为此,您需要 return 那里的承诺。
进入 then 处理程序不会发生“ 在 catch 块被解析之前 ”,catch 处理程序确实已经执行并且 return undefined - 这就是承诺链的延续。

Why doesn't the control move to the bottom of the then block where we would output "Why are we not here..."?

因为在记录 undefined 之后,您访问 res1.length,这会导致异常 TypeError: Cannot read property 'length' of undefined。这拒绝了未在任何地方处理的承诺,导致警告。


现在进入真正的问题:如何正确地做到这一点?您应该避免使用 Promise constructor antipattern! The .then() and .catch() calls already return a promise for the result of the handler, which you can and should use - no need to manually resolve or reject a promise, and no unhandled promise rejections because some inner promise is rejected but you fail to propagate this failure outside (notice the "Inside result..." handler should have been called when res1.length errored). Also I recommend to use .then(…, …) instead of .then(…).catch(…)(或 .catch(…).then(…))进行条件 success/error 处理。

function Test(User, N) {
    return f1(User.id).then(res1 => {
//  ^^^^^^
        if (res1.length < N) {
            return f2(User.gender).then(res2 => {
//          ^^^^^^
                const res3 = filterDups2(res1, res2)
                if (res3.length >= N)
                    return res3.slice(0,N).map(item => item.name)
//                  ^^^^^^
                else
                    throw new Error("Not Enough Data")
//                  ^^^^^
            }, err => {
                console.log(err.message)
                throw new Error("Not Enough Data")
//              ^^^^^
            })
        } else {
            return res1.slice(0,N).map(item => item.name)
//          ^^^^^^
        }
    }, err => {
        console.log(err.message)
        return f2(User.gender).then(res => {
//      ^^^^^^
            if (res.length < N) {
                throw new Error("Not Enough Data...")
//              ^^^^^
            } else {
                return res.slice(0,N).map(item => item.name);
//              ^^^^^^
            }
        }, err => {
            console.log(err.message)
            throw new Error("Both api calls have failed")
//          ^^^^^
        })
    })
}

避免一些代码重复:

function Test(User, N) {
    return f1(User.id).then(res1 => {
        if (res1.length >= N) return res1;
        return f2(User.gender).then(res2 => {
            return filterDups2(res1, res2)
        }, err => {
            console.log(err.message)
            return res1;
        })
    }, err => {
        console.log(err.message)
        return f2(User.gender).catch(err => {
            console.log(err.message)
            throw new Error("Both api calls have failed")
        })
    }).then(res => {
        if (res.length >= N)
            return res.slice(0,N).map(item => item.name)
        else
            throw new Error("Not Enough Data")
    })
}