SvelteKit UnhandledPromiseRejection with strange TLS Connection error - javascript

I have here a strange collection of error, for which i canno't find any solutions on the internet ( maby i'm just bad with google ).
I created a website, with the following files/folder tree:
With this setup i first got this error message:
Client network socket disconnected before secure TLS connection was established.
Which I didn't find any solution for other then reload the page.
After deciding handling the error myself i tried this:
api.js
import axios from 'axios';
import axiosRetry from 'axios-retry';
const base = process.env['API_ENDPOINT'];
axiosRetry(axios, { retries: 5 });
async function send({ path }) {
let pages = 0;
let resp;
try {
resp = await axios.get(`${base}/${path}`);
} catch (error) {
throw error;
}
if ('X-WP-TotalPages' in resp.headers) {
pages = resp.headers['X-WP-TotalPages'];
}
return {
pages: pages,
body: resp.data
};
}
export async function get(path) {
return send({ method: 'GET', path });
}
And i call it in (for example) landing.js:
import { writable } from 'svelte/store';
import { browser } from '$app/env';
import * as api from '$lib/api';
let loading = false;
let posts = [];
const list = writable({
loading,
posts
});
export default {
subscribe: list.subscribe,
async fetchNews() {
if (loading) return {};
loading = true;
list.set({ loading, posts });
let lang = 'de';
if (browser) {
lang = localStorage.getItem('lang') || 'de';
}
try {
const res = await api.get(`posts?filter[lang]=${lang}&per_page=4&_embed`);
} catch (error) {
throw error;
}
posts = await res.body;
posts.map((post) => {
if (post._embedded['wp:featuredmedia'])
post.image = post._embedded['wp:featuredmedia'][0].source_url;
else post.image = '/news/news_placeholder.png';
});
loading = false;
list.set({ loading, posts });
}
};
Now the new error is something like this:
[UnhandledPromiseRejection: 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(). The promise rejected with
the reason "Test".] { code: 'ERR_UNHANDLED_REJECTION' }
Maybe I'm just an idiot right, but i need really some help here!
Thanks in advance for this.

Try wrapping the whole function body in try {...}, instead of the const res = await api.get... line and observe where exactly the error happens.

Related

How to synchronously serialize api calls in react?

I have a task that requires fetching api data, with the constraint of only one outstanding api request at a time. Must receive a response, or time out, before issuing the next one. Since fetch (or axios) returns a promise, I can’t figure out how to wait for each promise to fulfill before issuing the next fetch.
I'm handed a large array of api url's that must all be resolved in this one-at-a-time manner before continuing.
I’m using create-react-app’s bundled dev server, and Chrome browser.
Curiously, accomplishing this via a node script is easy, because ‘await fetch’ actually waits. Not so in my browser environment, where all the fetch requests blast out at once, returning promises along the way.
Here’s a simple loop that results in the desired behavior as a node script. My question is how to achieve this one-outstanding-request-at-a-time synchronous serialization in the browser environment?
const fetchOne = async (fetchUrl) => {
try {
const response = await fetch(fetchUrl, { // Or axios instead
"headers": {
'accept': 'application/json',
'X-API-Key': 'topSecret'
},
'method': 'GET'
})
const data = await response.json();
if (response.status == 200) {
return (data);
} else {
// error handling
}
} catch(error) {
// different error handling
}
}
const fetchAllData = async (fetchUrlArray) => {
let fetchResponseDataArray = new Array();
let fetchResponseDataObject = new Object(/*object details*/);
for (var j=0; j<fetchUrlArray.length; j++) { // or forEach or map instead
// Node actually synchronously waits between fetchOne calls,
// but react browser environment doesn't wait, instead blasts them all out at once.
// Question is how to achieve the one-outstanding-request-at-a-time synchronous
// serialization in the browser environment?
fetchResponseDataObject = await fetchOne(fetchUrlArray[j]);
fetchResponseDataArray.push(fetchResponseDataObject);
}
return(fetchResponseDataArray);
}
If there's a problem, it's with code you haven't shown (perhaps in one of your components, or maybe in your project configuration).
Here's an runnable example derived from the problem you described, which mocks fetch and an API, showing you how to iterate each network request synchronously (and handle potential errors along the way):
Note, handling potential errors at the boundaries where they might occur is a better practice than only having a top level try/catch: by doing so, you can make finer-grained decisions about what to do in response to each kind of problem. Here, each failed request is stored as [url, error] in a separate array so that you can programmatically make decisions if one or more requests failed. (Maybe you want to retry them in a subsequent step, or maybe you want to show something different in the UI, etc.). Note, there's also Promise.allSettled(), which might be useful to you now or in the future.
<div id="root"></div><script src="https://unpkg.com/react#17.0.2/umd/react.development.js"></script><script src="https://unpkg.com/react-dom#17.0.2/umd/react-dom.development.js"></script><script src="https://unpkg.com/#babel/standalone#7.16.4/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="env,react">
const {useEffect, useState} = React;
const successChance = {
fetch: 0.95,
server: 0.95,
};
function mockApi (url, chance = successChance.server) {
// Simulate random internal server issue
const responseArgs = Math.random() < chance
? [JSON.stringify({time: performance.now()}), {status: 200}]
: ['Oops', {status: 500}];
return new Response(...responseArgs);
}
function mockFetch (requestInfo, _, chance = successChance.fetch) {
return new Promise((resolve, reject) => {
// Simulate random network issue
if (Math.random() > chance) {
reject(new Error('Network error'));
return;
}
const url = typeof requestInfo === 'string' ? requestInfo : requestInfo.url;
setTimeout(() => resolve(mockApi(url)), 100);
});
}
// Return an object containing the response if successful (else an Error instance)
async function fetchOne (url) {
try {
const response = await mockFetch(url);
if (!response.ok) throw new Error('Response not OK');
const data = await response.json();
return {data, error: undefined};
}
catch (ex) {
const error = ex instanceof Error ? ex : new Error(String(ex));
return {data: undefined, error};
}
}
async function fetchAll (urls) {
const data = [];
const errors = [];
for (const url of urls) {
const result = await fetchOne(url);
if (result.data) data.push([url, result.data]);
else if (result.error) {
// Handle this however you want
errors.push([url, result.error]);
}
}
return {data, errors};
}
function Example () {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const {data, errors} = await fetchAll([
'https://my.url/api/0',
'https://my.url/api/1',
'https://my.url/api/2',
'https://my.url/api/3',
'https://my.url/api/4',
'https://my.url/api/5',
'https://my.url/api/6',
'https://my.url/api/7',
'https://my.url/api/8',
'https://my.url/api/9',
]);
setData(data);
}
catch (ex) {
console.error(ex);
}
setLoading(false);
};
fetchData();
}, []);
return (
<div>
<div>Loading: {loading ? '...' : 'done'}</div>
<ul>
{
data.map(([url, {time}]) => (<li
key={url}
style={{fontFamily: 'monospace'}}
>{url} - {time}</li>))
}
</ul>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
</script>

Axios instance promise.all error handling

I have the following code in my own async function that uses another imported function from module which is a custom wrap of axios inside try/catch block:
async function getCharacter (realmSlug, characterName) {
try {
const [{id, name, gender, faction, race, character_class, active_spec, realm, guild, level, last_login_timestamp, average_item_level, equipped_item_level}, {pets, unlocked_battle_pet_slots},{mounts}] = await Promise.all([
getCharacterSummary(realmSlug, characterName), -- custom axios instance
getCharacterPetsCollection(realmSlug, characterName),
getCharacterMountsCollection(realmSlug, characterName)
])
....
return result;
} catch (error) {
console.log(error.code);
if (error.response.status === 404 || error.response.status === 403) {
console.error(`${getCharacter.name},${characterName}#${realmSlug}`);
}
return { name: characterName, realm: realmSlug }
}
}
The problem is that if I use promise.all according to Stackoverflow 1,2 I can not handle errors. So the problem is when I call function to execute, my errors doesn't handle in (catch) block. At all. Even if I don't need print them, anyway I receive messages in console about 404 errors, but console.log(error.code) still gives me nothing. For example:
So is there any way to handle this annoying error messages in console somehow?
For example using .catch somewhere? Or using for await ... of or rxJS instead if it's possible?
Exporting function and using .catch
Even if I export this function getCharacter in another .js file and use the following code:
const getCharacter = require('./getCharacter');
let bulkCharacters = [{realmSlug, characterName},{realmSlug, characterName},... ,n] //array of characters for getCharacter request
const promises = bulkCharacters.map(async ({realmSlug, characterName}) => {
try {
return await getCharacter(realmSlug, characterName);
} catch (e) {
console.log(e)
}
});
let test = await Promise.all(promises)
.catch(function(arrayOfPromises, err) {
// log that I have an error, return the entire array;
console.log('A promise failed to resolve', err);
return arrayOfPromises;
})
.then(function(arrayOfPromises) {
console.log(arrayOfPromises)
})
;
console.log('stop')
I still receive errors in console, without triggering catch block inside getCharacter function or this file in which this function was imported and catch block is outside the function.

Axios cancel token cancelling request before even called

So I am implementing axios call cancelation in the project. Right now looking at axios documentation it seems pretty straight forward https://github.com/axios/axios#cancellation
So I did define variables on the top of my Vue component like
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
obviously on top of that is import axios from 'axios';
Then I have a method of fetching the API
On the top of the method I want to cancel out the request in case it is running so the last one cancels out if the user spams the filtering.
async fetchPartners(inputToClear) {
source.cancel();
...
try {
const response = await axios.get(`../partners?limit=1000${this.createRequestString()}`, {
cancelToken: source.token
});
// Here you can see I did add the cancelToken to the request
this.partners = response.data.data;
} catch (error) {
if (axios.isCancel(error)) {
console.log('Request canceled', error.message);
}
const fetchErrors = this.utilGlobalHandleErrorMessages(error);
this.utilGlobalDisplayMessage(fetchErrors.message, { type: 'error' });
return [];
} finally {
...
}
},
So it is pretty straight forward, just took the code from axios documentation I gave you above, it should be working by logic. But what is actually happening, it doesn't even allow me to fetch the call, it is already cancelled out before I can call it. On console it shows me
Request canceled undefined
It just catches the error as if I am cancelling the call, but how can it be, because I am source.cancel() before the call.
Anyone has any idea?
I hope you should throttle your requests instead of canceling the request.
Could you please try the following if throttle does not suit your requirement?
const CancelToken = axios.CancelToken;
let source;
async fetchPartners(inputToClear) {
if(source){
source.cancel();
}
...
source = CancelToken.source();
try {
const response = await axios.get(`../partners?limit=1000${this.createRequestString()}`, {
cancelToken: source.token
});
// Here you can see I did add the cancelToken to the request
this.partners = response.data.data;
} catch (error) {
if (axios.isCancel(error)) {
console.log('Request canceled', error.message);
}
const fetchErrors = this.utilGlobalHandleErrorMessages(error);
this.utilGlobalDisplayMessage(fetchErrors.message, {
type: 'error'
});
return [];
} finally {
...
}
}

How to deal with Node error 'Unhandled promise rejection' so that page gets loaded anyway?

I'm creating a Next.js app served by a Node (Express) server which pulls in data through requests to an API. I keep my request endpoints in a separate api file:
const apiBase = 'http://www.example.com'
export default {
news: apiBase + '/news/'
// other endpoints
}
Then I do my requests in getInitialProps, and do conditional rendering based on whether the request gives an error or not:
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 is a helper function that looks like this:
export const asyncReq = endpoint => {
return
fetch( endpoint )
.then( res => { return res.ok ? res.json() : res } )
}
This all works fine both when the request is successful and when I get 404 or 500 errors. But suppose I intentionally use a wrong endpoint:
const apiBase = 'http://www.example.com'
export default {
news: wrongApiBase + '/news/'
// other endpoints
}
In this case, Node gives me the following error because wrongApiBase is undefined:
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)
which is what it should do, but the error causes the page to never get loaded. What am I supposed to do to handle the error? My idea was to chain a catch statement to asyncReq, but I'm not sure what I should return from it that I can then use in getInitialProps. I tried returning false but nothing changes, the page just doesn't load.
export const asyncReq = endpoint => {
return
fetch( endpoint )
.then( res => { return res.ok ? res.json() : res } )
.catch( err => { // What should I return here? )
}
+++ UPDATE +++
As it turns out, there was an issue with the error I was producing. Like I said, I was using a wrong variable name (wrongBaseApi) to trigger an error, which caused Node to never serve the page. In hindsight, it makes sense, as it's an error with Node code and not with the incoming request.
By using the right variable but assigning it a wrong value (an actually wrong API base, such as http://dahfjkdahfds.com, which is a not a Node error but an error with the request), I was able to make the solution of using a try/catch block offered by #iKoala and #DEVCNN work. So my code became:
static async getInitialProps( { query: { slug } } ) {
const news = await asyncReq( api.news + slug )
return { news }
}
render() {
// this.props.news.data
// this.props.news.error
}
and
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 }
}
}
I think you have to handle the error thrown from 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 };
}
}
Not a good approach but we can achieve by adding an event listener on the root node
window.addEventListener('unhandledrejection', rj => {
this.setState({hasError: true})
})
something like this.
Since it's NodeJS, I would use process.on() (instead of window.addEventListener()) with the unhandledRejection event:
process.on("unhandledRejection", (err, promise) => {
console.log(`Unhandled rejection (promise: ${promise}, reason: ${err})`);
});
Let's say your getInitialProps calls asyncReq which in turn calls throwError method. Now if throwError method throws an error, you can catch it with the catch block in getInitialProps. So you should always put a catch block in the function that starts the function chain. To diagnose errors better, you should put the catch block in each function. But as a general rule, catch block in the first function that you call is a must.
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 } )
}

Returning pending promises in async await

I am learning to use async / await and am having issues when trying to make an api request to set up my twilio device. Given the below code block, when I call device(), I get the following error message:
Uncaught
TwilioException {message: "Capability token is not valid or missing."}
message
:
"Capability token is not valid or missing."
__proto__
:
Object
I believe this is due to the fact that the json returned in the device function is still shown as pending. How do I resolve this and what am I doing wrong? Thanks.
Code block:
import {Device} from 'twilio-client';
const api = async () => {
try {
const response = await fetch('/api/twilio');
const json = await response.json();
if (response.status === 403) {
twilioConnectionFailure('Twilio has not been purchased.');
}
return json;
} catch (error) {
console.log(`Connection failed: ${error.message}`);
throw Error(error.message);
}
};
const device = () => {
const json = api();
Device.setup(json.token);
return Device;
};
export default device;
The api function is still asynchronous and returns a promise - you need to wait for it:
export default async function device() {
const json = await api();
// ^^^^^
Device.setup(json.token);
return Device;
}

Categories