Passing promises through functions React Native - javascript

I'm brand new to react native and I've been browsing through snippets of code and am confused as to how promises are passed along.
I have this event handler onRefresh() that is called when I pull down on a flatlist and I'm trying to have it use the return of apiSearchDB when it returns true/false.
onRefresh = () => {
this.setState({...}, () => {
return this.apiSearchDB()
.then(function(response) {
console.log(response);
})
.catch((error) => {
console.log(error);
});
})
}
apiSearchDB = () => {
return fetch('/some_endpoint')
.then((response) => response.json())
.then((json) => {
this.setState({
...
}, () => {return true})
return true;
}).catch((error) => {
console.error(error);
return false;
})
}
The line console.log(response); only prints undefined and I can't figure out why.
Could my handler also be written as
onSearch = () => {
return new Promise((resolve, reject) => {
var response = this.apiSearchDB();
response
? resolve();
: reject();
}
});
}
or onSearch = () => {...} and function onSearch(){...}?
Thank you in advance!

You should read more about using promises (good article - We have a problem with promises). However, two basic rules that will help you in this case are are:
The value returned from a promise is wrapped in a promise.
Promises can be chained.
The apiSearchDB should return a promise that contains the json as the resolved value, and error as the rejected value:
apiSearchDB = () =>
fetch('/some_endpoint')
.then((response) => response.json())
.then((json) => json)
// can be removed unless you want to do something with the error before passing it on
.catch((error) => Promise.reject(error));
The onRefresh (or onSearch) method should get the promise from apiSearchDB, and add it's own chain. Resolve promise should be handled with the then handler. If it's the rejected value, it will be handled by the catch handler:
onRefresh = () =>
this.apiSearchDB()
.then((response) => {
console.log(response);
// do something with response
this.setState({...}, () => {
});
return response;
})
.catch((error) => {
console.log(error);
// do something with error
this.setState({...}, () => {
});
});
}

Related

Event Listener running only once with Promise

I created a Promise here to connect to the storage and I also need to listen for changes.
With window.onstorage inside the promise it only listens once, even though it dispatches two events.
I can only listen to the two events dispatched if I put it directly in useEffect and without Promise.
Is it possible to listen to an event inside a Promise?
If so how should I do it and what is wrong?
CODE
const connect = useCallback(
() =>
new Promise<IConnectFunction>((resolve, reject) => {
Promise.all([infuraStorageData, metamaskStorageData])
.then(response => {
window.onstorage = e => {
resolve({
infuraData: response[0],
metamaskData: response[1],
});
};
})
.catch((error: DOMException) => reject(error));
}),
[infuraStorageData, metamaskStorageData]
);
useEffect(() => {
useStorageDBHook
.connect()
.then(e => {
console.log(e);
// window.onstorage = e => {
// console.log(e);
// };
})
.catch(e => {
console.log(e);
});
}, [useStorageDBHook]);
A Promise by design can only resolve once. Further call to resolve(value) has no effect.
If you want to receive (get notified) multiple events, you need some kind of stream interface (aka an observable, or subscribable), which will continuously feed you new data whenever available (in your case, whenever onstorage event fires).
A hand-rolled subscribable interface can be implemented like this:
class Subscribable {
constructor(source) {
this.subscribers = [];
source(this.notify.bind(this));
}
subscribe(sink) {
this.subscribers = this.subscribers.concat(push);
const unsubscribe = () => {
this.subscribers = this.subscribers.filter((_sink) => _sink !== sink);
};
return unsubscribe;
}
notify(newValue, error) {
this.subscribers.forEach((sink) => sink(newValue, error));
}
}
The usage is very close to Promise, you replace (resolve, reject) with a single notify callback, you cannot chain it with a .catch though, which is admittedly a bit drawback.
const connect = useCallback(
() =>
new Subscribable((notify) => {
// I took the liberty to change your code a bit
// cus it looks confusing, I guess your real intention is like:
window.onstorage = (e) => { notify(e) };
Promise.all([infuraStorageData, metamaskStorageData])
.then((response) => {
notify({
infuraData: response[0],
metamaskData: response[1],
});
})
.catch((error) => notify(null, error));
}),
[infuraStorageData, metamaskStorageData]
);
useEffect(() => {
const unsubscribe = connect().subscribe((newValue, error) => {
if (error) {
// handle error
} else {
console.log(newValue);
}
});
return unsubscribe;
}, [connect]);

Uncaught TypeError: Cannot read property 'then' of undefined at HTMLFormElement.<anonymous>

All code work good, but when I put then to the returned module all code crashes and throws error. Is the problem from that the export is function? If it is not from the function may someone explain why?
This is the module
export default {
search: function(searchTerm, searchLimit, sortBy) {
fetch(
`http://www.reddit.com/search.json?q=${searchTerm}&sort=${sortBy}&limit=${searchLimit}`
)
.then(res => res.json())
.then(data => data.data.children.map(data => data.data))
.catch(err => console.log(err));
}
};
This is actual main JavaScript file
import reddit from "./redditApi";
const searchForm = document.querySelector("#search-form");
const searchInput = document.querySelector("#search-input");
// form eventlistener
searchForm.addEventListener("submit", e => {
e.preventDefault();
// get search term
const searchTerm = searchInput.value;
// get sort
const sortBy = document.querySelector('input[name="sortby"]:checked').value;
// get limit
const searchLimit = document.querySelector("#limit").value;
// check input
if (searchTerm === "") {
// show message
showMessage("Please add a search Term!", "alert-danger");
}
// clear input
searchInput.value = "";
// search reddit
reddit.search(searchTerm, searchLimit, sortBy).then(results => {
console.log(results);
});
});
fetch returns a promise, but search doesn't have any return statement at all.
After you call catch on it, the promise is discarded.
If you want to use it outside the search function then you need to return it.
If you intend to do that
reddit.search(searchTerm, searchLimit, sortBy)
.then(results => {
console.log(results);
})
.catch(error => {
console.log(error);
});
you should wrap fetch inside a Promise.
example:
export default {
search: (searchTerm, searchLimit, sortBy) => {
return new Promise((resolve, reject) => {
fetch(`http://www.reddit.com/search.json?q=${searchTerm}&sort=${sortBy}&limit=${searchLimit}`)
.then(res => res.json())
.then(data => resolve(data.data.children.map(data => data.data)))
.catch(err => reject(err));
});
}
};

is it normal to pass callback into a async function and even wrap it again?

app.js
import test from "./asyncTest";
test().then((result)=>{
//handle my result
});
asyncTest.js
const test = async cb => {
let data = await otherPromise();
let debounce = _.debounce(() => {
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then( => response.json())
.then(json => json );
}, 2000);
};
export default test;
The fetch result "json" I intend to return is unable to be the return value of "test" function since the value only available in an inner function scope such as debounce wrapper. Since above reason, I tried to pass a callback function and wrap the callback to be Promise function(pTest) as below.
const test = async cb => {
let debounce = _.debounce(() => {
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then(response => response.json())
.then(json => cb(null, json))
.catch(err => cb(err));
}, 2000);
};
const pTest = cb => {
return new Promise((resolve, reject) => {
test((err, data) => {
if (err) reject(err);
resolve(data);
});
});
};
export default pTest;
This way works for me, but I'm wondering if it's correct or are there any ways to solve this scenario?
The fetch API already returns a promise. Wrapping it in another Promise object is actually an anti-pattern. it is as simple as the code below:
/*export*/ async function test() {
let data = await otherPromise();
return fetch("https://jsonplaceholder.typicode.com/posts/1")
.then(response => response.json())
.then(json => {
return {
json: json,
data: data
}
});
};
function otherPromise() {
return new Promise((resolve, reject) => {
resolve('test for data value');
});
}
// In index.js call
test().then(res => {
console.log(res)
});

Angular2 http.post: Create promise with subscribe

I´m trying do create a function, that returns a Promise as the code: (someprovider.ts)
postToPaymentApi(url:string, data:string, options:RequestOptions, order:Order):Promise<any>{
let result = this.http.post(url, data, options).map(res => res.json())
.subscribe(data => {
// all my logic here!
});
}, error => {
console.log(error)
})
return new Promise((resolve)=>{
resolve(result)
})
}
The problem is, when I call this function, I do not get the data, because this post take a few seconds to finish and I get the promise before the post finish.
this.postToPaymentApi(url, data, options, order).then(data => {
console.log(data);
})
What Am I doing wrong?
if you want to create a function that return promise, your function should be :
postToPaymentApi(url:string, data:string, options:RequestOptions, order:Order):Promise<any >{
return new Promise((resolve, reject) => {
this.http.post(url, data, options)
.map(res => res.json())
.subscribe(data => {
resolve(data);
}
}, error => {
console.log(error)
reject({error: error});
});
});
}
Andre, you want to use the toPromise operator to transform the observable returned by .map() into a promise. Return the result of that operator
return http.post(...).map(...).toPromise()
Now a proper promise is returned, so the calling code can use it as such:
postToPaymentApi(...).then(
data => console.log(data)
)

Extracting functions in a Promise chain

I am wanting to refactor a Promise chain by extracting out some functions. Currently I have
const getData = (uuid) => {
return new Promise((resolve) => {
fetch(
// go fetch stuff
)
.then((response) => {
if (!response.ok) {
return resolve(false);
}
return response;
})
.then(fetchres.json)
.then(response => {
// Do more stuff that requires resolves that I will also want to refactor
})
.catch(err => {
console.log(err);
resolve(false);
});
});
};
So I want to extract the part where I resolve the unsuccessful responses. But pass along any successful ones. I have pulled it out like so.
const resolveUnsuccessfulResponses = (response) => {
if (!response.ok) {
return response.resolve(false);
}
return response;
}
const getData = (uuid) => {
return new Promise((resolve) => {
fetch(
// go fetch stuff
)
.then(resolveUnsuccessfulResponses)
.then(fetchres.json)
.then(response => {
// Do more stuff that requires resolves that I will also want to refactor
})
.catch(err => {
console.log(err);
resolve(false);
});
});
};
Now I'm understandably getting the error resolve is not defined. How can I resolve this Promise in an external function?
Should I pass resolve to my extracted function? That would seem clunky.
.then(response => resolveUnsuccessfulResponses(response, resolve))
I might end up having something like
.then(fetchres.json)
.then(parseResponseData)
.then(postDataSomewhere)
.then(doOtherThings)
.then(doEvenMoreCoolThings)
And to have to pass response and resolve to each of them seems wrong
You should return a new Promise from your external functions aswell:
const resolveUnsuccessfulResponses = (response) => {
return new Promise((resolve, reject) => {
if (!response.ok) {
return resolve(false);
}
return resolve(response);
});
}

Categories

Resources