Как справиться с ошибкой узла «Отклонение необработанного обещания», чтобы страница все равно загружалась? - PullRequest
1 голос
/ 20 марта 2019

Я создаю приложение Next.js, обслуживаемое сервером Node (Express), который извлекает данные посредством запросов к API. Я храню конечные точки моего запроса в отдельном api файле:

const apiBase = 'http://www.example.com'

export default {
    news: apiBase + '/news/'
    // other endpoints
}

Затем я выполняю свои запросы в getInitialProps и выполняю условный рендеринг в зависимости от того, выдал ли запрос ошибку или нет:

static async getInitialProps( { query: { slug } } ) {
    const news = await asyncReq( api.news + slug )
    return { news, error: news.status }
}

render() {
    return this.props.error ? <Error /> : <News />
}

asyncReq - это вспомогательная функция, которая выглядит следующим образом:

export const asyncReq = endpoint => {
    return 
        fetch( endpoint )
        .then( res => { return res.ok ? res.json() : res } )
}

Все это прекрасно работает как при успешном запросе, так и при получении ошибок 404 или 500. Но предположим, что я намеренно использовал неверную конечную точку:

const apiBase = 'http://www.example.com'

export default {
    news: wrongApiBase + '/news/'
    // other endpoints
}

В этом случае Node выдает мне следующую ошибку, поскольку wrongApiBase не определено:

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: 498)

это то, что он должен делать, но ошибка приводит к тому, что страница никогда не загружается. Что я должен сделать, чтобы справиться с ошибкой? Моя идея заключалась в том, чтобы связать оператор catch с asyncReq, но я не уверен, что мне следует возвращать из него, чтобы потом использовать в getInitialProps. Я попытался вернуть false, но ничего не изменилось, страница просто не загружается.

export const asyncReq = endpoint => {
    return 
        fetch( endpoint )
        .then( res => { return res.ok ? res.json() : res } )
        .catch( err => { // What should I return here? )
}

+++ ОБНОВЛЕНИЕ +++

Как оказалось, возникла проблема с ошибкой, которую я выдал. Как я уже сказал, я использовал неправильное имя переменной (wrongBaseApi), чтобы вызвать ошибку, из-за которой Node никогда не обслуживал страницу. Оглядываясь назад, это имеет смысл, поскольку это ошибка с кодом узла, а не с входящим запросом.

Используя правильную переменную, но присвоив ей неправильное значение (фактически неверная база API, такая как http://dahfjkdahfds.com, что является не ошибкой узла, а ошибкой запроса), я смог найти решение: используя блок try / catch, предлагаемый @iKoala и @DEVCNN work. Итак, мой код стал:

static async getInitialProps( { query: { slug } } ) {
    const news = await asyncReq( api.news + slug )
    return { news }
}

render() {
    // this.props.news.data
    // this.props.news.error
}

и

export async function asyncReq( endpoint ) {
    try {
        const res = await fetch( endpoint )
        return { 
            data: res.ok ? await res.json().then( val => { return val } ) : null,
            error: res.ok ? false : res.status
        } 
    } catch( error ) {
        return { error } 
    }
}

Ответы [ 4 ]

2 голосов
/ 20 марта 2019

Я думаю, что вы должны обработать ошибку, выданную asyncReq:

static async getInitialProps( { query: { slug } } ) {
  try {
    const news = await asyncReq( api.news + slug )
    return { news, error: news.status }
  } catch (err) {
    // any error, including http error 40x and 50x
    return { news: null, error: err };
  }
}
0 голосов
/ 20 марта 2019

Допустим, ваш getInitialProps вызывает asyncReq, который, в свою очередь, вызывает метод throwError. Теперь, если метод throwError выдает ошибку, вы можете перехватить ее с помощью блока catch в getInitialProps. Поэтому вы всегда должны помещать блок catch в функцию, которая запускает цепочку функций. Чтобы лучше диагностировать ошибки, вы должны поместить блок catch в каждую функцию. Но, как правило, блок catch в первой вызываемой вами функции является обязательным.

getInitialProps = async function( { query: { slug } } ) {
try{
const news = await asyncReq( 'http://localhost:3000/' + slug )
return { news, error: news.status }
}
catch(err){
console.log('err:', err);
}
}

const throwError = () => {
throw 'New Error'
}

const asyncReq = endpoint => {
throwError();
return 
    fetch( endpoint )
    .then( res => { return res.ok ? res.json() : res } )
}
0 голосов
/ 20 марта 2019

Поскольку это NodeJS, я бы использовал process.on() (вместо window.addEventListener()) с событием unhandledRejection:

process.on("unhandledRejection", (err, promise) => {
    console.log(`Unhandled rejection (promise: ${promise}, reason: ${err})`);
});
0 голосов
/ 20 марта 2019

Не очень хороший подход, но мы можем добиться этого, добавив прослушиватель событий в корневой узел

 window.addEventListener('unhandledrejection', rj => {
      this.setState({hasError: true})
    })

примерно так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...