ожидайте в блоке finally функции asyn c PromiseRejectionHandledWarning - PullRequest
0 голосов
/ 21 апреля 2020

Я использую async await в своем коде NodeJs, и структура кода выглядит следующим образом.

async function main(){
    try {
        await someFunctionThatReturnsRejectedPromise()
    } catch(e) {
        console.log(e)
    }
}

async function someFunctionThatReturnsRejectedPromise() {
    try {
        await new Promise((resolve,reject) => {
            setTimeout(() => {
                reject('something went wrong')
            }, 1000);
        })
    } catch(e) {
        return Promise.reject(e)
    } finally {
        await cleanup() // remove await here and everything is fine
    }
}


function cleanup() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('cleaup successful')
        }, 1000);
    })
}

main();

В блоке finally я делаю некоторые async очистка, которая обязательно разрешит. Но этот код выдает PromiseRejectionHandledWarning

(node:5710) UnhandledPromiseRejectionWarning: something went wrong
(node:5710) 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(). (rejection id: 1)
(node:5710) [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.
something went wrong
(node:5710) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)

Насколько я понимаю, я не оставляю здесь никаких обещаний без внимания. Что я делаю неправильно? Должен ли finally блокироваться синхронно по замыслу? Если да, то почему?

Обновление 1:

Если я конвертирую someFunctionThatReturnsRejectedPromise в доброе старое then и catch, это работает без проблем :

function someFunctionThatReturnsRejectedPromise() {
    return (new Promise((resolve,reject) => {
        setTimeout(() => {
            reject('something went wrong')
        }, 1000);
    })).catch(e => {
        return Promise.reject(e)
    }).finally(() => {
        return cleanup()
    })
}

Обновление 2: (Понял проблему)

Если я await вернул Обещание, проблема решена.

 return await Promise.reject(e)

И это заставляет меня понять, что я делал неправильно. Я нарушал цепочку await (частично синоним того, что не возвращался синтаксис Promise в then / catch). Спасибо всем:)

Ответы [ 2 ]

3 голосов
/ 21 апреля 2020

Когда Promise отклоняется, оно должно быть обработано до того, как текущий стек вызовов очистит , иначе произойдет необработанное отклонение. У вас есть:

} catch (e) {
  return Promise.reject(e)
} finally {
  await cleanup() // remove await here and everything is fine
}

Если вы удалите await, someFunctionThatReturnsRejectedPromise немедленно вернет после создания отклоненного обещания, поэтому Promise.reject(e), отклоненное обещание, будет поймано на catch в main сразу после. Но если есть какая-либо задержка, отклоненное Обещание будет не обработано немедленно; Ваше await cleanup() будет означать, что отклоненное Обещание будет оставаться необработанным в течение определенного периода времени, прежде чем someFunctionThatReturnsRejectedPromise вернется, что означает, что main catch не может обработать отклоненное Обещание во времени.

Другой метод, который вы могли бы использовать, - обернуть ошибку в Error вместо Promise.reject, а затем проверить, является ли результат instanceof Error в main:

window.addEventListener('unhandledrejection', () => console.log('unhandled rejection!'));

async function main() {
  const result = await someFunctionThatReturnsRejectedPromise();
  if (result instanceof Error) {
    console.log('Error "caught" in main:', result.message);
  }
}

async function someFunctionThatReturnsRejectedPromise() {
  try {
    await new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('something went wrong')
      }, 1000);
    })
  } catch (e) {
    return new Error(e);
  } finally {
    await cleanup()
  }
}


function cleanup() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('cleaup successful')
    });
  })
}

main();
1 голос
/ 21 апреля 2020

Обновленный ответ замените

Promise.reject(e) на throw e;

, чтобы функция стала

async function someFunctionThatReturnsRejectedPromise() {
   try {
       await new Promise((resolve,reject) => {
           setTimeout(() => {
               reject('something went wrong')
           }, 1000);
       })
   } catch(e) {
       throw e;
   } finally {
       await cleanup() // remove await here and everything is fine
   }
}

Причина

someFunctionThatReturnsRejectedPromise метод сначала отклоняет Promise. Итак, поток управления перешел к методу main catch block. Позже cleanup метод пытается сделать то же самое. Который должен отказаться от уже отклоненного обещания. Таким образом, вы получаете ошибку

Promise.reject, немного отличающуюся от предложения throw. Пожалуйста, обратитесь throw против Promise.reject

Именно поэтому удаление await из cleanup() или удаление return из cleanup метод работает. Потому что это отсоединит Promise от текущего потока управления.

...