I'm working with node. I have an api setup as a netlify serverless function , so that I can run a number of processes in parallel.
As part of this I want so create an array of unexecuted fetch object promises, which I can then execute in paralell using something like:
const responses = await Promise.allSettled(promiseArray);
So far I have:
for (let i = 0; i < groupedRecords.length; i++) {
const groupedRecord = groupedRecords[i];
const fetchObjects = await createFetchObjectArray(groupedRecord);
}
async function createFetchObjectArray(records) {
const fetchURL = (obj) => fetch('http://localhost:8888/.netlify/functions/meta1', {
method: 'POST',
body: JSON.stringify(obj),
headers: { 'Content-Type': 'application/json' }
});
let outputArray = [];
for (let i = 0; i < (records.length); i++) {
const record = await records.pop();
const obj = { "_id": record._id };
const fetchObj = fetchURL(obj);
outputArray.push(fetchObj);
}
return outputArray;
}
I was hoping to get an array of promises into 'fetchObjects', however they appear to be trying to execute. I'm seeing:
FetchError: request to http://localhost:8888/.netlify/functions/meta1 failed, reason: connect ECONNREFUSED 127.0.0.1:8888
how to I keep the fetch objects from executing?
Calling fetchURL will execute it immediately. await or Promise.allSettled are not mechanisms to start asynchronous operations, they are mechanisms to wait for an operation that has already kicked off.
If you want to build an array of functions and not call them immediately, instead of:
const fetchObj = fetchURL(obj);
outputArray.push(fetchObj);
You probably just want:
outputArray.push(() => fetchURL(obj));
If you later on want to use Promise.allSettled, you could do this as such:
Promise.allSettled(outputArray.map(foo => foo());
Here's a cleaned up version of your code that also fixes more bugs:
for(const groupedRecord of groupedRecords) {
const fetchObjects = createFetchObjectArray(groupedRecord);
// Assuming you're doing something with fetchObjects after?
}
function fetchURL(obj) {
return fetch('http://localhost:8888/.netlify/functions/meta1', {
method: 'POST',
body: JSON.stringify(obj),
headers: { 'Content-Type': 'application/json' }
});
}
function createFetchObjectArray(records) {
return records.map(record => {
const obj = { "_id": record._id };
return () => fetchURL(obj);
});
}
Related
This question already has answers here:
How to return many Promises and wait for them all before doing other stuff
(6 answers)
Closed 4 months ago.
i created a function named getCartItems which calls getSingleItems passing id as argument. if i log the json result in getSingleItem it is showing object of the product correctly but when i try to access the function call value i get a promise how to resolve it?
const getSingleItem = async (id)=>{
const response = await fetch("http://localhost:4000/products/"+id, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
})
const json = await response.json();
return json;
}
const getCartItems = () => {
let currArr = JSON.parse(localStorage.getItem('cart'));
let newArr = currArr.map( async (el)=>await getSingleItem(el.id))
console.log(newArr);
setCartItems(newArr);
}
useEffect(()=>{
getCartItems();
}, [])
if try to use for loop instead of map it shows promise pending and throws connection error.
You need to resolve the promises from the map method with Promise.all and await for it before setting the cartItems state.
const getSingleItem = async (id)=>{
const response = await fetch("http://localhost:4000/products/"+id, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
})
const json = await response.json();
return json;
}
const getCartItems = async () => {
let currArr = JSON.parse(localStorage.getItem('cart'));
let newArr = await Promise.all(currArr.map( async (el)=>await getSingleItem(el.id)))
console.log(newArr);
setCartItems(newArr);
}
useEffect(()=>{
getCartItems();
}, [])
I am trying to create a chrome extension using the gmail api. I am using an api call to get certain info, and then loop through the results and execute another api call per result. Once those calls finish, then the rest of the code will run. The issue that I am facing right now is that the rest of the code is executing before the fetches are returning the info.
Below is the function that is not working. I added 4 flags in the code to illustrate the problem better. The correct behavior should output flag 1, then loop 2, then loop 3, then 4. However, the actual behavior outputs flag 1 then loop 2 then 4, and then looping through flag 3.
One theory I have is that the cause is that I am using fetches inside of .then(). If this is the issue, how do I fix this? Or does it have anything to do with fetch being async? Any advice or helpful links would be super appreciated.
Thanks so much!
let senderList = new Array();
let init = {
method: 'GET',
async: true,
headers: {
Authorization: 'Bearer ' + token,
'Content-Type': 'application/json'
},
'contentType': 'json'
};
fetch(
'https://gmail.googleapis.com/gmail/v1/users/me/messages?key=ABCDEFGHIJKLMNOPQRSTUVWXYZ1234',
init)
.then((response) => response.json())
.then(function(data) {
console.log("Flag 1")
let messageResponse = data.messages; //Array of Message Objects
for (let i = 0; i < messageResponse.length; i++) {
console.log("Flag 2")
fetch(
'https://gmail.googleapis.com/gmail/v1/users/me/messages/' + messageResponse[i].id +
'?key=ABCDEFGHIJKLMNOPQRSTUVWXYZ1234',
init)
.then((response) => response.json())
.then(function(message) {
let headers = message.payload.headers;
console.log("Flag 3")
for (let i = 0; i < headers.length; i++) {
if (headers[i].name == 'From'){
senderList.push(headers[i].value);
}
}
});
}
}).then(function() {
console.log("Flag 4")
onSuccess(senderList);
});
}
Use async/await syntax with Promise.all that resolves when all promises are fulfilled:
run();
async function run() {
const list = await getSenderList();
console.log(list, list.errors);
}
async function getSenderList() {
const API_URL = 'https://gmail.googleapis.com/gmail/v1/users/me/messages/';
const API_KEY = '?key=...';
const INIT = {method: 'GET', /* ... etc. */ };
const data = await fetchJson(API_URL + API_KEY);
const senderList = [];
senderList.errors = {};
await Promise.all(data.messages.map(async msg => {
const msgData = await fetchJson(API_URL + msg.id + API_KEY, INIT);
if (msgData instanceof Error) {
senderList.errors[msg.id] = msgData;
return;
}
const headers = msgData.payload.headers;
for (const h of headers) {
if (h.name === 'From') {
senderList.push(h.value);
}
}
}));
return senderList;
}
function fetchJson(url, init) {
return fetch(url, init).then(r => r.json()).catch(err => err);
}
I have two main functions. The first one gets the SOAP data from an API. The second one parses it to json and then formats it into the object I need to store. I have been logging and seeing the return values, and as of so far until now, it's been fine. However, I am calling exports.createReturnLabelfrom my test file and all I can see is Promise { <pending> } . I am returning a value at the last function. Does this code look weird to your? How can I clean it up?
const soapRequest = require('easy-soap-request');
const xmlParser = require('xml2json')
exports.createReturnLabel = async () => {
const xml = hidden
const url = 'https://ws.paketomat.at/service-1.0.4.php';
const sampleHeaders = {
'Content-Type': 'text/xml;charset=UTF-8',
};
const auth = async () => {
const {
response
} = await soapRequest({
url: url,
headers: sampleHeaders,
xml: xml,
timeout: 2000
});
const {
body,
statusCode
} = response;
return body
}
const fetchLabel = async () => {
const soapData = await auth();
try {
const json = xmlParser.toJson(soapData)
const labelData = JSON.parse(json)["SOAP-ENV:Envelope"]["SOAP-ENV:Body"]["ns1:getLabelResponse"]
return {
courier: 'dpd',
tracking_number: labelData["return"]["paknr"],
barCode: labelData["return"]["barcodecontent"],
printLabel: labelData["return"]["label"],
_ref: null
}
} catch (e) {
return (e)
}
}
return fetchLabel()
}
calling from my test file return console.log(file.createReturnLabel())
There's an async function call inside your code.
Should be: return await fetchLabel(), so that it awaits for completion before going on its merry way.
I have been trying to figure this out for quite some time and it's too confusing so i thought i'd ask.
var listenKey = "";
const createListenKey = async () => {
await axios({
url: "/api/v3/userDataStream",
method: "POST",
baseURL: "https://api.binance.com",
headers: {
"X-MBX-APIKEY":
"H48w9CLuTtTi955qWjcjjEKhh0Ogb3jnnluYucXXXXXXXXXXXXXXXX",
},
}).then((response) => {
var key = response.data.listenKey;
console.log(key, "created");
return key;
});
};
listenKey = createListenKey();
listenKey.then((key) => {
console.log(key);
});
the console.log in the last but one line is printing undefined. Why is that?
Thanks in advance!
You did not return anything from the async function createListenKey
const asynF = async ()=>{
Promise.resolve(1).then(res=>{
//Simulating response from axios call
console.log(res)
})
// you are not returning anyting from this function equivalent of => return ;
}
asynF().then(res=>{
//this would log undefined
console.log(res)
})
As you know async function returns a promise you have two options make the outer wrapper a async function as well and just use await like below
const key = await createListenKey(config)
or else
you could simply do this
return createListenKey(config).then(res=>{
listenKey = res
})
can't say much more without knowing context.
Might I suggest not to mix then and async wait together
Because the createListenKey() function doesn't return anything. The return statement in the then clause inside that function is scoped in the then block. To return a value from an async function, you need to do something like this.
const createListenKey = async () => {
const response = await axios({
url: "/api/v3/userDataStream",
method: "POST",
baseURL: "https://api.binance.com",
headers: {
"X-MBX-APIKEY":
"H48w9CLuTtTi955qWjcjjEKhh0Ogb3jnnluYucXXXXXXXXXXXXXXXX",
},
})
var key = response.data.listenKey;
console.log(key, "created");
return key;
};
I have a snippet (below) that will generate requests based on a couple of params. It essentially creates load similar to JBehaves by distinguishing requests per user. For the most part, this works fine. Generation of requests works as expected. The results however are not working as one would expect using Promise.all(). Which leads me to my question:
Is there an issue with Promise.all()?
The format of the results may look a little wonky in this question but, essentially I'm just creating an array of users (which in itself is just an array of request results).
Actual Results
Instead of each object within the array being different, they're all the same. In most case, it appears to be the last object pushed into the array (but I haven't fully confirmed this). This behavior initially lead me to believe there was a scoping issue within my snippet but, I haven't been able to find it :(
[
[{
hostname: 'google.com',
headers: [Object],
path: '/url1/',
method: 'GET',
date: 1457395032277,
status: 200,
ttfb: 1488
}, {
hostname: 'google.com',
headers: [Object],
path: '/url1/',
method: 'GET',
date: 1457395032277,
status: 200,
ttfb: 1488
}]
]
Expected Results
I would expect that Promise.all() would return a (promise) resolving to an array with multiple objects - each one being different as to reflect the results from each task as defined in tasks().
[
[{
hostname: 'google.com',
headers: [Object],
path: '/url1/',
method: 'GET',
date: 1457395032277,
status: 200,
ttfb: 1488
}, {
hostname: 'bing.com',
headers: [Object],
path: '/url2/',
method: 'GET',
date: 1457395032280,
status: 500,
ttfb: 501
}]
]
Code Snippet
If you notice the commented out console.dir(stats): this line spits out the results as expected (different results per task) but, if I slap a .then() on the end of the reduce, the array is returned as Actual Results (vs. Expected Results)
(For brevity, let's assume request() returns a promise)
'use strict';
const request = require('./request');
const Promise = require('bluebird');
module.exports = (tests, options) => {
return Promise.all(users());
////////////
/* generate users */
function users() {
let users = [];
for (let x = options.users; x > 0; x--) {
/* https://github.com/petkaantonov/bluebird/issues/70#issuecomment-32256273 */
let user = Promise.reduce(tasks(), (values, task) => {
return task().then((stats) => {
// console.dir(stats);
values.push(stats);
return values;
});
}, []);
users.push(user);
};
return users;
}
/* generate requests per user */
function tasks() {
let tasks = [];
for (let id of Object.keys(tests)) {
for (let x = options.requests; x > 0; x--) {
let task = () => {
let delay = options.delay * 1000;
return Promise.delay(delay).then(() => request(tests[id]));
};
tasks.push(task);
};
}
return tasks;
}
};
Request Snippet
'use strict';
const https = require('follow-redirects').https;
const sprintf = require('util').format;
const Promise = require('bluebird');
process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;
let request = (req) => {
return new Promise((resolve) => {
let start = Date.now();
let ttfb;
let cb = (res) => {
req.status = res.statusCode;
res.on('data', (chunk) => {
if (!ttfb) ttfb = Date.now() - start;
});
res.on('end', () => {
req.ttfb = ttfb;
req.end = Date.now() - start;
resolve(req);
});
res.on('error', (err) => {
req.error = err;
resolve(req);
});
};
/* convert cookies for convenience */
if (req.headers.cookies) {
let cookies = [];
for (let cookie of Object.keys(req.headers.cookies)) {
cookies.push(sprintf('%s=%s', cookie, req.headers.cookies[cookie]));
}
req.headers.cookie = cookies.join('; ');
req.cookies = req.headers.cookies;
delete req.headers.cookies;
}
https.request(req, cb).end();
});
};
module.exports = request;
Using
$ npm --version
2.14.12
$ node --version
v0.12.9
Any help would be greatly appreciated!
Is there an issue with Promise.all()?
No.
Instead of each object within the array being different, they're all the same.
Indeed. They are all the same object.
Your request function does - for whatever reason - resolve its returned promise with its argument. As you are passing the very same tests[id] object to all requests of a batch, they all will end up with this object.
Your console.dir did show the expected results because request does mutate its argument - the test object contains different values after every call, which are subsequently logged, before being overwritten in the next call.
You should change that cb to create a new object, instead of mutating req:
function cb(response) {
let result = {
status: response.statusCode
};
response.on('data', (chunk) => {
if (!ttfb) ttfb = Date.now() - start;
});
response.on('end', () => {
result.ttfb = ttfb;
result.end = Date.now() - start;
resolve(result);
});
response.on('error', (err) => {
result.error = err;
resolve(result);
});
}