防止"未处理的承诺拒绝"错误

时间:2017-08-26 12:12:15

标签: node.js promise

在我的服务器应用程序中,我想要返回"禁止"当用户没有端点权限时的值。

为此,我创建了一个被拒绝的重用承诺:

let element = document.createElement('div')
element.setAttribute('class', '{a}')
element.setAttribute('id', '{a}')
element.setAttribute('data-x', '{b}')

let data = {
  a: 'ok',
  b: 'okok'
}

injector(element, data)

console.log(
  `${element.outerHTML} === <div class="ok" id="ok" data-x="okok"></div>`,
  element.outerHTML === '<div class="ok" id="ok" data-x="okok"></div>'
)

function injector (htmlElement, data) {
  let allAttributes = getAllAttributes(htmlElement)
  let reduced = allAttributes.reduce((acc, currentAttribute) => {
    let currentAttributeValue = htmlElement
      .getAttribute(currentAttribute)
      .replace(/(\{)|(\})/g, '')
    if (acc.hasOwnProperty(currentAttributeValue)) {
      acc[currentAttributeValue] = acc[currentAttributeValue].concat(currentAttribute)
    } else {
      acc[currentAttributeValue] = [currentAttribute]
    }
    return acc
  }, {})
  Object.keys(reduced)
    .forEach(key => {
      reduced[key].forEach(attribute => {htmlElement.setAttribute(attribute, data[key])})
    })
}

function getAllAttributes (htmlElement) {
  let outerHtml = htmlElement.outerHTML
  let tagName = htmlElement.tagName
  let outerHtmlWithoutTagName = outerHtml.replace(
    new RegExp(tagName.toLowerCase(), 'g')
    , ''
  )
  return outerHtmlWithoutTagName.replace(
    /(="{([\w|.]+)}")|(\<|\>|\/)/g
    , ''
  )
    .trim()
    .split(' ')
}

然后应用程序中的其他地方:

export const forbidden = Promise.reject(new Error('FORBIDDEN'))

然而,当我启动我的应用程序时,我收到警告

import {forbidden} from './utils'

...

    resolve: (root, {post}, {db, isCollab}) => {
        if (!isCollab) return forbidden
        return db.posts.set(post)
    },

我怎样才能告诉Node这个Promise可以不被处理?

3 个答案:

答案 0 :(得分:3)

  

我创建了一个被拒绝的重用承诺

嗯,不要,创建一个重用函数可能要容易得多:

export function forbidden() { return Promise.reject(new Error('FORBIDDEN')); }

每次调用它时,也会为错误提供适当的堆栈跟踪。

  

我怎样才能告诉Node这个Promise可以不被处理?

只是无所事事地处理它:

export const forbidden = Promise.reject(new Error('FORBIDDEN'));
forbidden.catch(err => { /* ignore */ }); // mark error as handled

(并且不要忘记包含关于这个看似无操作声明的目的的评论)。

答案 1 :(得分:1)

我会提出偏离公认答案的建议。我不建议使用return语句来提供Error-这忽略了throw的确切意图!

我认为您的直觉和接受的答案都经过精心设计。只需执行以下操作即可:

if (!isCollab) throw new Error('FORBIDDEN');

如果您不希望进行堆栈跟踪,则无需考虑所有开销,只需执行以下操作:

if (!isCollab) throw 'FORBIDDEN';

答案 2 :(得分:0)

  • OP的用法没有完全描述,但是OP的注释“顺便说一句,我不想​​为每个禁止的错误创建堆栈跟踪,因为我不想泄漏有关我的应用程序的详细信息。我宁愿只创建一次拒绝。” 使我相信,OP的动机至少有一部分是防止未处理的forbidden拒绝导致信息泄漏。

  • 返回被拒绝(但被拒绝)的诺言在同步功能与异步功能中的行为有所不同。在前者中,诺言是逐字返回的。在后者中,将其重新包装为承诺并自动重新抛出(等同于从函数内部抛出)。 。 。 。

返回forbidden时,同步功能与异步功能之间的行为差​​异:

async function test(){
  try {
    let a = await (async()=>{return forbidden;})();  
  } catch(e){console.log(e.message);} // outputs: 'FORBIDDEN'
  try {
    let a = (()=>{return forbidden;})();
    // error undetected  
  } catch(e){console.log(e.message);} // never reaches here !!!
  console.log("something else"); // outputs: something else
  let a=(()=>{return forbidden;})();  // UHR + '#<Promise>' + no addr
  console.log("something else"); // outputs: something else
  await (async()=>{return forbidden;})();  // UHR + '#<Promise>' + no addr leak}
}
test();
  • 不管OP的使用方式如何,未处理的拒绝都会泄露程序信息,这是一个有效的问题。

下面的工厂函数makeError将提供一个通用的解决方案,它基于OP的原始灵感:

const verboten=new Error('verbotten');
const makeError = () => verboten;
async function test2(){
  try {
    await (async()=>{throw makeError();})();  
  } catch(e){console.log(e.message);} // outputs: 'verboten'
  // uncomment the following to trigger UHR (unhandled rejection)
  //await (async()=>{throw makeError();})();  // UHR + 'verboten' + no addr leak
}

请注意,makeError返回常量对象verboten,而不是其自身。 (是的,这是允许的,尽管很少使用。)因此,堆栈跟踪是一个固定值,与程序中的错误位置无关,就像原始OP的forbidden一样。

这对于发行版本来说很好,但是对于开发版本,可以对makeError进行较小的更改,在该版本中查看堆栈很有用:

const makeError = Error;