I have written a function to pull stream data from the fetch API. I want to convert the function to a recursive one and would like to write it as an async function.
Here is the code which works but not asynchronous:
const productsStream = async () => {
const request = {
searchRange: ['fragrance', 'perfumery', 'cologne'],
exclusionCategory: ['discounted']
}
const response = await fetch('/products/stream', {
method: 'POST',
body: JSON.stringify(request)
});
const decoder = new TextDecoderStream();
const reader = response.body.pipeThrough(decoder).getReader();
let line = "";
while (true) {
const { done, value } = await reader.read();
let chunk = remainder + value;
let products = []
let newlineIndex;
while ((newlineIndex = chunk.indexOf("\n")) != -1) {
let item = chunk.substring(0, newlineIndex);
chunk = chunk.substring(newlineIndex);
if (chunk.length > 1) chunk = chunk.substring(1);
}
line = chunk;
products = []
if ( done ) break;
}
}
I'd welcome any help in converting this.
Related
I have a javascript app that reads invoices. It's working perfectly but I would like to improve it's performance a bit.
The way the app works is this. There are Next and Submit buttons. After either, a new image is loaded and a function doOcr is called. In each instance of doOCR, a worker is created which must take time. I am trying to figure out how to move the worker outside the function so it doesn't need to be created each time. Code is below. Thoughts?
//ocr the image and fill values
function doOCR(){(async () => {
worker = Tesseract.createWorker();
await worker.load();
await worker.loadLanguage('eng');
await worker.initialize('eng');
const values = [];
for (let i = 0; i < rectangles.length; i++) {
const { data: { text } } = await worker.recognize(imgFile, { rectangle: rectangles[i] });
values.push(text);
}
console.log(values);
//calculate and display result
dir = values[0].split(' ');
$("#inDir").val(dir[1]);
//get invoice number
$("#inVendor").val(dir[0]);
//get invoice #
var str = values[0];
var n = str.indexOf("Invoice:", 0);
var n1 = str.indexOf("Invoice", n+5);
invoice = str.substr(n+9, n1-n-9);
$("#inInvoice").val(invoice);
await worker.terminate();
$('#status').attr('src','check.png').show();
})()};
Thank you.
I think I got it by changing the top half of the function to:
function doOCR(){(async () => {
if (typeof(worker) == "undefined") {
worker = Tesseract.createWorker();
await worker.load();
await worker.loadLanguage('eng');
await worker.initialize('eng');
}
const values = [];
for (let i = 0; i < rectangles.length; i++) {
const { data: { text } } = await worker.recognize(imgFile, {rectangle: rectangles[i]});
values.push(text);
}
console.log(values);
[ ... ]
and removed
await worker.terminate();
I have written a program to extract links to download photos in three steps:
The getPersons() function get the complete list of people to traverse.
Get the photos download links from the list of persons.
Download from the list created in step 2.
I am trying to refactor step 2 into an async function.
Is there an easy way to refactor second step into a function to make the code more readable?
Ideally, I would like to retrieve all the links and only then, start the download.
const axios = require("axios");
const cheerio = require("cheerio");
const url = "https://www.website.com";
const persons = [];
async function getPersons() {
await axios.get(url).then(response => {
const html = response.data;
const $ = cheerio.load(html);
const personList = $(".bio-btn");
console.log(personList.length);
personList.each(function() {
const link_raw = $(this).attr("href");
const link = url + link_raw;
const name = link_raw.replace("/bio/", "");
person.push({
name,
link
});
});
});
}
getPersons().then(function() {
persons.forEach(async function(person) {
var personLink = person.link;
await axios.get(personLink).then(response => {
const html = response.data;
const $ = cheerio.load(html);
const snapshots = $(".ratio-4-3");
snapshots.each(function() {
const pic = $(this).attr("style");
if (pic != undefined && pic.includes("biopicture")) {
var bioPhoto = s[1];
}
});
});
});
});
You are hardly getting much benefit out of your asynchronicity, as you end up making serial requests. I'd write it this way (untested):
async function getPersons() {
const response = await axios.get(url);
const html = response.data;
const $ = cheerio.load(html);
const personList = $('.bio-btn');
const persons = [];
personList.each(function() {
const link_raw = $(this).attr('href');
const link = url + link_raw;
const name = link_raw.replace("/bio/", "");
persons.push({
name,
link,
});
});
return persons;
};
async function getSnapshots() {
const persons = await getPersons();
const linkPromises = persons.map(person => axios.get(person.link));
const linkResponses = await Promise.all(linkPromises);
linkResults.forEach(response => {
const html = response.data;
const $ = cheerio.load(html);
const snapshots = $(".ratio-4-3");
// ...
});
}
I would refactor it like this. Removing .then() methods and the function keyword on anonymous functions makes the code look cleaner.
Using Promise.all() enables you to start all the downloads asynchronously which could be better than downloading images one by one.
const axios = require('axios');
const cheerio = require('cheerio');
const url = 'https://www.website.com';
async function getPersons() {
const response = await axios.get(url);
return extractPersonList(response);
}
// Step 1
function extractPersonList(response) {
const persons = [];
const html = response.data;
const $ = cheerio.load(html);
const personList = $('.bio-btn');
console.log(personList.length);
personList.each(() => {
const link_raw = $(this).attr('href');
const link = url + link_raw;
const name = link_raw.replace('/bio/', '');
persons.push({
name,
link
});
});
return persons;
}
async function getPhotos() {
const persons = await getPersons();
const promisies = persons.map(p => axios.get(p.link));
// Step 2
const responses = await Promise.all(promisies);
// Step 3
responses.forEach(response => {
const html = response.data;
const $ = cheerio.load(html);
const snapshots = $('.ratio-4-3');
snapshots.each(() => {
const pic = $(this).attr('style');
if (pic && pic.includes('biopicture')) {
var bioPhoto = s[1];
}
});
});
}
// Call getPhotos to start the process
getPhotos();
I am trying to get API data so i can render it on Webpage using ReactJS. I tried many different ways to fetch api where its stored in array. But i am unable to do it.
Note:https://www.hatchways.io/api/assessment/workers/<worker_id> Here i'm looping so the
worker id gets add the of url.
const fetchAPI = e => {
let array = [];
const api2 = `https://www.hatchways.io/api/assessment/workers/`;
for (var i = 0; i <= 4; i++) {
array.push(api2 + i);
}
return array;
};
console.log(fetchAPI());
Thanks in advance.
You need to hit the url first. Async/Await would be a good choice.
<script>
async function check()
{
var arrayData =[];
var url = "https://www.hatchways.io/api/assessment/workers/";
for(var i=1;i<=4;i++)
{
const response = await fetch(url+""+i);
const myJson = await response.json();
arrayData.push(myJson);
}
console.log(arrayData)
}
check();
</script>
Use Promise.all for example:
const baseURL = 'https://jsonplaceholder.typicode.com';
const fetchUsers = fetch(`${baseURL}/users`);
const fetchPosts = fetch(`${baseURL}/posts`);
Promise.all([fetchUsers, fetchPosts]).then((responses) => {
const responsesToJson = responses.map(response => response.json());
return Promise.all(responsesToJson);
}).then((jsonResponse) => {
const [userResponse, postResponse] = jsonResponse;
console.log(userResponse);
console.log(postResponse);
});
Mediocre javascript developer here and need some help..
I want to make a GET call to a url several times in a for loop.
I am trying to use fetch, but because of the promise/timing situation, I can't figure out how to make it work.
Below is the code and it's entirely possible that the fetch approach doesn't make sense for what I am trying to do. Would appreciate your help either helping me with code or telling me I am idiot and advising an alternative :)
var fromAmt = 100;
var fromOOP = 50;
var fromGM = 50;
var fromCur = "USD"
var toCur = ["USD","EUR","INR","GBP","SGD"];
var adjAmt = [];
async function getConversionAsync(fcur,tcur,amt)
{
let response = await fetch('https://data.fixer.io/api/convert?access_key=xyxyxyxyxyxyxy&from=' + fcur + '&to=' + tcur + '&amount=' + amt);
let data = await response.json()
return data;
}
for (i = 0; i < toCur.length; i++) {
getConversionAsync(fromCur,toCur[0].toString(),fromAmt)
.then(data => display(data));
}
function display(thing){
adjAmt.push(thing.result);
}
document.getElementById("something").innerHTML = adjAmt[0].toString();
In your example, document.getElementById("something").innerHTML = adjAmt[0].toString(); is executed before anything is pushed to adjAmt. You need to wait for the loop calls to finish before displaying a result, and for this you could wrap everything inside an async function.
const fromAmt = 100;
const fromOOP = 50;
const fromGM = 50;
const fromCur = 'USD';
const toCur = ['USD', 'EUR', 'INR', 'GBP', 'SGD'];
const adjAmt = [];
const getConversionAsync = async (fcur, tcur, amt) => {
const response = await fetch(`https://data.fixer.io/api/convert?access_key=xyxyxyxyxyxyxy&from=${fcur}&to=${tcur}&amount=${amt}`);
return response.json();
}
function display(thing) {
adjAmt.push(thing.result);
}
(async () => {
for (i = 0; i < toCur.length; i += 1) {
const data = await getConversionAsync(fromCur, toCur[0], fromAmt);
display(data);
}
document.getElementById('something').innerHTML = adjAmt[0].toString();
})();
Some small changes to make it work without the API call, but you'll want to access the correct index in your loop. I don't know what the exact output you're wanting here but in this case I just joined all the values in the array.
Additionally, the setting of innerHTML needs to be done once all the values are retrieved from the API, so I would even suggest doing that when the loop terminates, or some other "done" type event.
Additionally, you can use Promise.all instead of a loop, which is what I would go with personally.
var fromAmt = 100;
var fromOOP = 50;
var fromGM = 50;
var fromCur = "USD"
var toCur = ["USD", "EUR", "INR", "GBP", "SGD"];
var adjAmt = [];
async function getConversionAsync(fcur, tcur, amt) {
let response = await sampleRequest()
/* let data = await response.json() */
return response;
}
for (i = 0; i < toCur.length; i++) {
const data = getConversionAsync(fromCur, toCur[i].toString(), fromAmt).then(data => {
display(data)
})
}
function display(thing) {
adjAmt.push(thing);
document.getElementById("something").innerHTML = adjAmt.join(', ')
}
function sampleRequest() {
return new Promise((resolve, reject) => {
resolve(Math.round(Math.random() * 1000))
})
}
<div id="something"></div>
I'm practicing my javascript skills doing a joke machine that collects jokes from an API and displays them, my problem is that currently, I'm using .then() which is fine, but I want to know how do I get my results in an array that doesn't have to wait for .then() like in fetchJokes().then(jokes => console.log(jokes));
I used to have a function that navigated the array like this:
let counter = 0;
let jokes = [joke1, joke2, joke3, joke4];
function nextJoke(jokes) {
counter++;
counter %= jokes.length; // start from 0 if we get to the end of the array
return jokes[counter]; // give us back the actual item
}
That's how it was used before fetching from the API.
And this is my current code:
const url = "https://api.icndb.com/jokes/random/5";
const fetchButton = document.querySelector("button[type=button][value=Fetch]");
async function fetchJokes() {
let jokes = [];
let data = await (await fetch(url)).json();
for (let i = 0; i < 5; i++) {
jokes[i] = data.value[i].joke;
}
return jokes;
}
fetchButton.addEventListener("click", () => {
fetchJokes().then(jokes => console.log(jokes));
});
<button type="button" value="Fetch">Fetch Jokes</button>
So I have figured out how to use buttons and all that jazz, just asking for help on how to pass the object that im receiving from fetchJokes() and store it in an Array.
You can use fetch() to retrieve the url's data. Then you have to transform it to json with .then((resp) => resp.json()).
After that, you can store the data inside your jokes array.
const url = "https://api.icndb.com/jokes/random/5";
const fetchButton = document.querySelector("button[type=button][value=Fetch]");
const nextButton = document.querySelector("button[type=button][value=Next]");
const display = document.getElementById("display");
let counter = 0;
let jokes = [];
nextButton.style.visibility = 'hidden'; //Hide next button
fetchButton.addEventListener("click", () => {
fetch(url).then((resp) => resp.json()) // Transform the data into json
.then(data => (jokes = data.value, //Set the array of jokes
fetchButton.style.visibility = 'hidden', //Hide fetch button
nextButton.style.visibility = 'visible')); //Show next button
});
nextButton.addEventListener("click", () => {
//Show next joke
display.innerHTML = jokes[counter++ % jokes.length].id + " : " + jokes[counter++ % jokes.length].joke;
});
<button type="button" value="Fetch">Fetch Jokes</button>
<button type="button" value="Next">Next joke</button>
<p id="display"></p>
This is what worked for me without modifying the main purpose of the snippet in the question.
let counter = 0;
const url = "https://api.icndb.com/jokes/random/5";
const fetchButton = document.querySelector("button[type=button][value=Fetch]");
async function fetchJokes() {
let jokes = [];
let data = await (await fetch(url)).json();
for (let i = 0; i < 5; i++) {
jokes[i] = data.value[i].joke;
}
return jokes;
}
fetchButton.addEventListener("click", () => {
fetchJokes().then(data => (jokes = data)).then(data => console.log(data));
});
<button type="button" value="Fetch">Fetch Jokes</button>
I'm not sure I understand the question...If you want to fetch from an async api and not have to use .then or have to wait, then that is not possible.
From what I understand of the question, if you do not want to use .then every time, only fetch when the array is empty (only fetch once):
const url = "https://api.icndb.com/jokes/random/5";
const fetchButton = document.querySelector("button[type=button]
[value=Fetch]");
let arr = []
let counter = 0;
async function fetchJokes() {
let data = await (await fetch(url)).json();
for (let i = 0; i < 5; i++) {
arr[i] = data.value[i].joke;
}
}
function nextJoke(jokes) {
const item = jokes[counter]
counter++;
if (counter >=jokes.length){
counter %= jokes.length;
}// start from 0 if we get to the end of the array
return item; // give us back the actual item
}
fetchButton.addEventListener("click", () => {
if (arr.length == 0){
fetchJokes()
.then(()=>console.log(nextJoke(arr)))
}else{
console.log(nextJoke(arr))
}
});
DEMO codepen
There are many ways to do this. I would suggest looking more into Javascript Promises.