Promise.all 与 try/catch 模拟

Promise.all with try/catch simulation

我目前正在 运行 学习并尝试熟悉 promise,我将切入介绍性概念,直达主题。在 NodeJS 中,使用库 BlueBird。我不想推迟函数调用,我也不想过多地污染代码,即使这是介绍性编码以熟悉前提,因为当尝试更高级的概念时,我会得到迷失在他们身上。我尝试将 'asyn/await' 与 try 块一起使用,但天哪,那段代码很乱,而且没有用…… 或多或少的准则:

Promises 包含一个内置的 catch 机制,如果处理标准的单个 Promise,它可以完美地工作。

// Try/Catch style Promises
funcTwo = function(activate) {
  return new Promise(function(resolve, reject) {
    var tmpFuncTwo;
    if (activate === true) {
      tmpFuncTwo = "I'm successful"
      resolve(tmpFuncTwo)
    } else if (activate === false) {
      tmpFuncTwo = "I'm a failure.";
      reject(tmpFuncTwo)
    } else {
      tmpFuncTwo = "Oh this is not good."
      throw new Error(tmpFuncTwo);
    }
  });
}

funcTwo(true)
  .then(val => {
    console.log("1: ", val)
    return funcTwo()
  })
  .catch(e => {
    console.log("2: Err ", e.message)
  })

让我有点困惑的是试图坚持与 Promise.all 相同的前提,错误没有被处理,因为 throw 直接推送到主控制器。 从此代码段抛出的异常永远不会进入 Catch 块。

funcThree = function(val) {
  return new Promise(function(resolve, reject) {
    if (val > 0)
      resolve((val + 1) * 5)
    else if (val < 0)
      reject(val * 2)
    else
      throw new Error("No work for 0");
  })
}
// Output in Dev Console
/* 
  Extrending to the catch block handling, This will fail, the exception is thrown, and ignores the catch block. Terminating the program.
*/
Promise.all([funcThree(1), funcThree(0), funcThree(-3)])
  .then(function(arr) {
    for (var ind = 0; ind < arr.length; ind++) {
      console.log(arr)
    };
  }, function(arr) {
    console.log(arr)
  })
  .catch(function(e) {
    console.log("Error")
  })

我尝试了一个简单的解决方法,但我对这种语言有些陌生,不确定这是否符合 "Best Practices",因为它们是从 Python 准则。

// Promise all, exceptionHandling
funcThree = (val) => {
  return new Promise(function(resolve, reject) {
    if (val > 0)
      resolve((val + 1) * 5)
    else if (val < 0)
      reject(val * 2)
    else {
      var tmp = new Error("No work for 0");
      tmp.type = 'CustomError';
      reject(tmp);
    }
  })
}

/*
  This works, and doesn't cause any type of mixup
*/
Promise.all([funcThree(1), funcThree(0), funcThree(-3)])
  .then(
    arr => {
      for (var ind = 0; ind < arr.length; ind++) {
        console.log(arr)
      };
    }, rej => {
      if (rej.type == 'CustomError')
        throw rej;
      console.log(arr)
    })
  .catch(e => {
    console.log("Catching Internal ", e.message)
  })

这是使用 Native Promise 库,以及 bluebird

有没有办法更自然地处理这个问题,

关于 jfriend00 的评论。我的意思是我不希望除了 try-catch 块之外的任何东西处理异常。当我尝试使用与普通 promise 相同的格式时,一切都完美对齐,并且我的 catch 得到确认,并且错误得到处理。由于 Promise.all 只能 resolve/reject 我认为没有一种干净的方法可以委托第二个代码片段中第二次调用 funcTwo 时抛出的异常。或多或少,我不确定我所做的解决方法“拒绝,检查拒绝是否传递了错误,如果传递了则抛出它”是一个好的解决方案,还是会导致一些深层次的问题随着代码的扩展。

Since Promise.all can only ever resolve/reject I don't think that there is a clean way of delegating the exception that is thrown from the second call to funcTwo in the second code snippet.

在您的代码块中:

// Try/Catch style Promises
funcTwo = function(activate) {
  return new Promise(function(resolve, reject) {
    var tmpFuncTwo;
    if (activate === true) {
      tmpFuncTwo = "I'm successful"
      resolve(tmpFuncTwo)
    } else if (activate === false) {
      tmpFuncTwo = "I'm a failure.";
      reject(tmpFuncTwo)
    } else {
      tmpFuncTwo = "Oh this is not good."
      throw new Error(tmpFuncTwo);
    }
  });
}

throwreject() 没有区别。 throw 被 Promise 构造函数捕获并变成 reject()。就个人而言,我更喜欢在这种情况下只使用 reject(),因为我认为函数调用比异常快一点。

我不知道这是否已编入规范,但通常认为使用错误对象拒绝是个好主意。所以,我会这样写你的代码:

function funcTwo(activate) {
  return new Promise(function(resolve, reject) {
    if (activate === true) {
      resolve("I'm successful");
    } else {
      let errMsg = activate === false ? "I'm a failure." : "Oh this is not good.";
      reject(new Error(errMsg));
    }
  });
}

承诺解决或拒绝。没有与拒绝不同的第三种错误情况。例外只会变成拒绝。所以,如果你有 return 的三个状态(如上面的代码),那么你必须决定如何将这三个状态放入 resolvereject

由于只是示例代码,这里不做具体推荐。如果 activate === false 实际上不是一个错误,只是一种不同类型的完成,它不应该中止你在 Promise.all() 中的其他承诺,那么你希望这种情况是 resolve(),而不是一个reject()。但是,没有硬性规定什么是什么 - 它实际上仅取决于您希望对呼叫者而言自然和简单的行为,因此它因情况而异。

此外,如果您不控制此处 funcTwo 中的代码,那么您可以在将其传递给 Promise.all() 之前在其上放置一个 .catch() 处理程序如果您希望 Promise.all() 逻辑以这种方式工作,您可以将特定拒绝变成解决方案。 Promises 链,因此您可以在将它们传递给更高级别的操作之前修改它们的输出。它类似于在较低级别使用 try/catch 来捕获和处理异常,因此较高级别的代码不必看到它(有时适当)。

More or less I'm not sure if what I've done as a workaround," reject, check if reject passed forward an error, and then throw it if it did", is a good solution or if it will cause some deep problem as code expands.

在此处的 Promise.all() 代码中:

/*
  This works, and doesn't cause any type of mixup
 */
Promise.all([funcThree(1), funcThree(0), funcThree(-3)]).then(arr => {
  for (var ind = 0; ind < arr.length; ind++) {
    console.log(arr[index]);
  }
}, rej => {
  if (rej.type == 'CustomError')
    throw rej;
  console.log(arr)
}).catch(e => {
  console.log("Catching Internal ", e.message)
})

您的第一个拒绝处理程序并没有真正帮助您。它没有做任何你不能在一个 .catch() 处理程序中做的事情。让我重复一遍。承诺只有两个结果 rejectresolve。例外没有第三个结果。在 promise 回调中发生的异常只会变成拒绝。所以,你上面的代码可以改成这样:

/*
  This works, and doesn't cause any type of mixup
*/
Promise.all([funcThree(1), funcThree(0), funcThree(-3)]).then(arr => {
  for (var ind = 0; ind < arr.length; ind++) {
    console.log(arr)
  };
}).catch(e => {
  // there is no value for arr here, no result - only a reject reason
  console.log("Catching Internal ", e.message)
  if (rej.type === "CustomError") {
      // do something special for this type of error
  }
  // unless you rethrow here, this rejection will be considered handled
  // and any further chained `.then()` will see the promise as resolved
  // but since there is no return value, the promise will be resolved
  // with an undefined result
});

如果你想尽早捕获 funcThree() 中的拒绝,以便它可以允许 Promise.all() 中的其余 Promise 仍然被跟踪并仍然得到它们的结果,那么你可以得到Promise.settle() 实现,无论有多少拒绝,都将遵循所有承诺得出结论,或者您可以编写自己的特殊情况:

function funcFour(val) {
    return funcThree(val).catch(err => {
       // catch and examine the error here
       if (err.type === "CustomError") {
           // allow things to continue on here for this specific error
           // and substitute null for the value.  The caller will have
           // to see null as a meaningful result value and separate from
           // a non-null result
           return null;
       } else {
           // Not an error we recognize, stop further processing
           // by letting this promise reject
           throw err;
       }
    });
}

Promise.all([funcFour(1), funcFour(0), funcFour(-3)]).then(arr => {
   // got results, some might be null
   console.log(arr);
}).catch(err => {
   // got some error that made it so we couldn't continue
   console.log(err);
});

我知道这都是风格观点,但是使用 promises 的好代码的好处之一是你不再有那么深的缩进代码,现在我看到人们放置了各种额外的缩进和根本不是的行需要并且似乎消除了干净承诺编码的一些好处。

在这种情况下,.then().catch()可以按照他们所遵循的承诺走在同一条线上。而且,无需在新行上开始内联函数定义。