JavaScript Promises - reject vs. throw

时间:2015-10-30 21:48:40

标签: javascript promise

I have read several articles on this subject, but it is still not clear to me if there is a difference between Promise.reject vs. throwing an error. For example,

Using Promise.reject

return asyncIsPermitted()
    .then(function(result) {
        if (result === true) {
            return true;
        }
        else {
            return Promise.reject(new PermissionDenied());
        }
    });

Using throw

return asyncIsPermitted()
    .then(function(result) {
        if (result === true) {
            return true;
        }
        else {
            throw new PermissionDenied();
        }
    });

My preference is to use throw simply because it is shorter, but was wondering if there is any advantage of one over the other.

7 个答案:

答案 0 :(得分:229)

There is no advantage of using one vs the other, but, there is a specific case where throw won't work. However, those cases can be fixed.

Any time you are inside of a promise callback, you can use throw. However, if you're in any other asynchronous callback, you must use reject.

For example,

new Promise(function() {
  setTimeout(function() {
    throw 'or nah';
    // return Promise.reject('or nah'); also won't work
  }, 1000);
}).catch(function(e) {
  console.log(e); // doesn't happen
});

won't trigger the catch, instead you're left with an unresolved promise and an uncaught exception. That is a case where you would want to instead use reject. However, you could fix this by promisifying the timeout:

function timeout(duration) { // Thanks joews
  return new Promise(function(resolve) {
    setTimeout(resolve, duration);
  });
}

timeout(1000).then(function() {
  throw 'worky!';
  // return Promise.reject('worky'); also works
}).catch(function(e) {
  console.log(e); // 'worky!'
});

答案 1 :(得分:158)

另一个重要的事实是GetStringFromDynamicResourceFile("MyFile.resx", "MyString1"); 不会reject()语句一样终止控制流。相反,return确实终止了控制流程。

示例:



throw




VS



new Promise((resolve, reject) => {
  throw "err";
  console.log("NEVER REACHED");
})
.then(() => console.log("RESOLVED"))
.catch(() => console.log("REJECTED"));




答案 2 :(得分:43)

Yes, the biggest difference is that reject is a callback function that gets carried out after the promise is rejected, whereas throw cannot be used asynchronously. If you chose to use reject, your code will continue to run normally in asynchronous fashion whereas throw will prioritize completing the resolver function (this function will run immediately).

An example I've seen that helped clarify the issue for me was that you could set a Timeout function with reject, for example:

new Promise(_, reject) {
 setTimeout(reject, 3000);
});

The above could would not be possible to write with throw.

In your small example the difference in indistinguishable but when dealing with more complicated asynchronous concept the difference between the two can be drastic.

答案 3 :(得分:36)

TLDR: 当函数有时会返回一个promise并且有时会抛出异常时,很难使用它。编写异步函数时,更喜欢通过返回被拒绝的承诺来表示失败

你的具体例子模糊了它们之间的一些重要区别:

由于您在承诺链内部错误处理,因此抛出的异常会自动转换到拒绝的承诺。这可以解释为什么它们似乎是可以互换的 - 它们不是。

考虑以下情况:

checkCredentials = () => {
    let idToken = localStorage.getItem('some token');
    if ( idToken ) {
      return fetch(`https://someValidateEndpoint`, {
        headers: {
          Authorization: `Bearer ${idToken}`
        }
      })
    } else {
      throw new Error('No Token Found In Local Storage')
    }
  }

这将是一种反模式,因为您需要同时支持异步和同步错误情况。它可能看起来像:

try {
  function onFulfilled() { ... do the rest of your logic }
  function onRejected() { // handle async failure - like network timeout }
  checkCredentials(x).then(onFulfilled, onRejected);
} catch (e) {
  // Error('No Token Found In Local Storage')
  // handle synchronous failure
} 

不好,这里正是Promise.reject(在全球范围内可用)拯救并有效区别于throw的地方。重构现在变成:

checkCredentials = () => {
  let idToken = localStorage.getItem('some_token');
  if (!idToken) {
    return Promise.reject('No Token Found In Local Storage')
  }
  return fetch(`https://someValidateEndpoint`, {
    headers: {
      Authorization: `Bearer ${idToken}`
    }
  })
}

现在,您只需使用一个catch()用于网络故障 同步错误检查是否存在令牌:

checkCredentials()
      .catch((error) => if ( error == 'No Token' ) {
      // do no token modal
      } else if ( error === 400 ) {
      // do not authorized modal. etc.
      }

答案 4 :(得分:6)

尝试的一个例子。只需将isVersionThrow更改为false即可使用reject而不是throw。

const isVersionThrow = true

class TestClass {
  async testFunction () {
    if (isVersionThrow) {
      console.log('Throw version')
      throw new Error('Fail!')
    } else {
      console.log('Reject version')
      return new Promise((resolve, reject) => {
        reject(new Error('Fail!'))
      })
    }
  }
}

const test = async () => {
  const test = new TestClass()
  try {
    var response = await test.testFunction()
    return response 
  } catch (error) {
    console.log('ERROR RETURNED')
    throw error 
  }  
}

test()
.then(result => {
  console.log('result: ' + result)
})
.catch(error => {
  console.log('error: ' + error)
})

答案 5 :(得分:6)

有一个区别-没关系-其他答案没有涉及,所以:

没有什么可能重要的,不。是的,相差很小。

如果传递给then的实现处理程序抛出了,则对then的调用返回的诺言将被抛出。

如果它返回被拒绝的承诺,则调用then返回的承诺将被解析为(并且最终将被拒绝,因为已解决的承诺被拒绝了) ,这可能会引入一个额外的异步“滴答”(在微任务队列中再增加一个循环,以浏览器的术语来说就是这样)。

但是,任何依赖于这种差异的代码都从根本上被破坏了。 :-)对承诺结算的时间应该不那么敏感。

这是一个例子:

function usingThrow(val) {
    return Promise.resolve(val)
        .then(v => {
            if (v !== 42) {
                throw new Error(`${v} is not 42!`);
            }
            return v;
        });
}
function usingReject(val) {
    return Promise.resolve(val)
        .then(v => {
            if (v !== 42) {
                return Promise.reject(new Error(`${v} is not 42!`));
            }
            return v;
        });
}

// The rejection handler on this chain may be called **after** the
// rejection handler on the following chain
usingReject(1)
.then(v => console.log(v))
.catch(e => console.error("Error from usingReject:", e.message));

// The rejection handler on this chain may be called **before** the
// rejection handler on the preceding chain
usingThrow(2)
.then(v => console.log(v))
.catch(e => console.error("Error from usingThrow:", e.message));

如果运行该代码,截至撰写本文时,您将获得:

Error from usingThrow: 2 is not 42!
Error from usingReject: 1 is not 42!

注意订单。

将其与相同的链进行比较,但都使用usingThrow

function usingThrow(val) {
    return Promise.resolve(val)
        .then(v => {
            if (v !== 42) {
                throw new Error(`${v} is not 42!`);
            }
            return v;
        });
}

usingThrow(1)
.then(v => console.log(v))
.catch(e => console.error("Error from usingThrow:", e.message));

usingThrow(2)
.then(v => console.log(v))
.catch(e => console.error("Error from usingThrow:", e.message));

这表明拒绝处理程序以其他顺序运行:

Error from usingThrow: 1 is not 42!
Error from usingThrow: 2 is not 42!

我在上面说“可能”是因为在其他类似情况下,其他领域的一些工作消除了这种不必要的额外滴答声。如果,所有涉及的诺言都是本国诺言(不仅是可兑现的诺言)。 (具体来说:在async函数中,return await x最初引入了一个与return x相比额外的异步刻度,同时在其他方面相同; ES2020对其进行了更改,以便如果x是本机承诺,多余的刻度将被删除。)

同样,任何对承诺履行时间非常敏感的代码都已经被破坏。因此,实际上,这无关紧要。

实际上,正如其他答案所提到的:

  • Kevin B pointed out一样,throw在您回退在履行处理程序中使用过的其他函数的情况下将不起作用-这是最大的事情
  • 就像lukyer pointed out一样,throw会突然终止该函数,这很有用(但是您在示例中使用的是return,它具有相同的作用)
  • 作为Vencator pointed out,您不能在条件表达式(throw)中使用? :,至少not for now

除此之外,这主要是风格/偏好的问题,因此,与大多数这样的人一样,要与您的团队达成共识(或者您不在乎这两种方式),并且保持一致。

答案 6 :(得分:3)

区别在于三元运算符

  • 您可以使用
return condition ? someData : Promise.reject(new Error('not OK'))
  • 您不能使用
return condition ? someData  : throw new Error('not OK')