Confusion with promises - javascript

I am getting stuck with trying to get JavaScript promise to work as intended.
My code:
var p = new Promise(function(resolve, reject) {
for (var i = 0; i < pics_needed.length; i++) {
download_pics(pics_needed[i])
}
for (var i = 0; i < partners_pics_needed.length; i++) {
partners_download_pics(partners_pics_needed[i])
}
resolve('Success!');
})
p.then(function() {
AsyncStorage.setItem("database",responseText)
AsyncStorage.removeItem("time")
alert ("Success! \nYour update has been installed.")
go()
});
Both functions that are called in the for loop download pictures from a server. The problem is, the p.then part of the function is running before all the pictures are downloaded. How can I alter this so that the p.then part happens after all the downloading is complete?
What the functions do:
function download_pics (id){
var path = RNFS.DocumentDirectoryPath + '/' + id + '.jpg';
fetch('address of server ='+id)
.then((response) => response.text())
.then((responseText) => {
var pic_object = JSON.parse(responseText)
RNFS.writeFile(path, pic_object.response, 'base64')
});
}

As hinted in the comments - promises are not worthless if you're going to go behind their backs.
function download_pics (id){
var path = RNFS.DocumentDirectoryPath + '/' + id + '.jpg';
return fetch('address of server ='+id) // <--- NOTE: return here
.then((response) => response.text())
.then((responseText) => {
var pic_object = JSON.parse(responseText)
return RNFS.writeFile(path, pic_object.response, 'base64') // <-- and here
});
}
and then
var pics_promises = pics_needed.map(function(pic) {
return download_pics(pic);
});
var partners_pics_promises = partners_pics_needed.map(function(pic) {
return partners_download_pics(pic);
});
return Promise.all(pics_promises.concat(partners_pics_promises));
EDIT: added the RNFS.writeFile to the promise chain per #adeneo (I'm not familiar with RNFS).

Related

How to SEND the content when making a GET in Javascript?

I need to GET all the data of a workbook, and I tried one method, but it does not work properly.. The problem is that the Promise is returning the first line of the first worksheet, but it does not continue the process for the rest of the rows of the worksheet, respectively, all the worksheets and I don't know what to do. Maybe you know..
So I wrote this code:
const SheetGet = {
getSheet: (req, res) => {
return new Promise ((resolve, reject) => {
XlsxPopulate.fromFileAsync(filePath)
.then(workbook => {
const wb = xlsx.readFile(filePath, {cellDates: true});
const sheetNames = wb.SheetNames;
sheetNames.forEach(function (element){
let endOfData = false;
let i = 2;
let dataArr = [];
while (endOfData === false){
let taskId = workbook.sheet(element).cell("A" + i.toString()).value();
if (taskId !== undefined){
res.send({
type: 'GET',
list_name: element,
task_id: taskId,
task_name: workbook.sheet(element).cell("B" + i.toString()).value(),
task_description: workbook.sheet(element).cell("C" + i.toString()).value(),
task_due_date: workbook.sheet(element).cell("D" + i.toString()).value(),
task_priority: workbook.sheet(element).cell("E" + i.toString()).value(),
task_status: workbook.sheet(element).cell("F" + i.toString()).value(),
task_notes: workbook.sheet(element).cell("G" + i.toString()).value()
});
i++;
}
else {
endOfData = true;
}
}
})
});
})
}
}
It only gets this an then it stops, and I need to get all of the data from the worksheet.
Do you have any idea on how to resolve this issue? Or the proper way to make it work? Thank you very very much for your time and help!!! Much appreciated every help!!
P.S. I tried this code with "console.log", and it works very well, but the problem is when I changed to res.send, in order to get the info to Postman.
I assume you are using express as the framework, the problem is when you use res.send method, the server already send the data to client, while the rest of the code still running in the background. What I'm gonna do with this case are like this.
const SheetGet = {
getSheet: (req, res) => {
return new Promise ((resolve, reject) => {
XlsxPopulate.fromFileAsync(filePath)
.then(workbook => {
const wb = xlsx.readFile(filePath, {cellDates: true});
const sheetNames = wb.SheetNames;
sheetNames.forEach(function (element){
let endOfData = false;
let i = 2;
let dataArr = [];
while (endOfData === false){
let taskId = workbook.sheet(element).cell("A" + i.toString()).value();
if (taskId !== undefined){
dataArr.push({ // this one
type: 'GET',
list_name: element,
task_id: taskId,
task_name: workbook.sheet(element).cell("B" + i.toString()).value(),
task_description: workbook.sheet(element).cell("C" + i.toString()).value(),
task_due_date: workbook.sheet(element).cell("D" + i.toString()).value(),
task_priority: workbook.sheet(element).cell("E" + i.toString()).value(),
task_status: workbook.sheet(element).cell("F" + i.toString()).value(),
task_notes: workbook.sheet(element).cell("G" + i.toString()).value()
});
i++;
}
else {
endOfData = true;
}
}
})
return res.json({ data: dataArr }); // this one
});
})
}
}
While there is some unnecessary code, the least change you can do to make it works are the code above.
Cheers.

NodeJS Loop issue due to async/synchronicity issues

I am porting an old ruby script over to use javascript setting the function as a cron instance so it will run on schedule. The function queries our mysql database and retrieves inventory information for our products and then sends requests to a trading partners api to update our inventory on their site.
Due to nodes a-synchronicity I am running into issues. We need to chunk requests into 1000 items per request, and we are sending 10k products. The issue is each request is just sending the last 1000 items each time. The for loop that is inside the while loop is moving forward before it finishes crafting the json request body. I tried creating anon setTimeout functions in the while loop to try and handle it, as well as creating an object with the request function and the variables to be passed and stuffing it into an array to iterate over once the while loop completes but I am getting the same result. Not sure whats the best way to handle it so that each requests gets the correct batch of items. I also need to wait 3 minutes between each request of 1000 items to not hit the request cap.
query.on('end',()=>{
connection.release();
writeArray = itemArray.slice(0),
alteredArray = [];
var csv = json2csv({data: writeArray,fields:fields}),
timestamp = new Date(Date.now());
timestamp = timestamp.getFullYear() + '-' +(timestamp.getMonth() + 1) + '-' + timestamp.getDate()+ ' '+timestamp.getHours() +':'+timestamp.getMinutes()+':'+timestamp.getSeconds();
let fpath = './public/assets/archives/opalEdiInventory-'+timestamp+'.csv';
while(itemArray.length > 0){
alteredArray = itemArray.splice(0,999);
for(let i = 0; i < alteredArray.length; i++){
jsonObjectArray.push({
sku: alteredArray[i]['sku'],
quantity: alteredArray[i]["quantity"],
overstockquantity: alteredArray[i]["osInv"],
warehouse: warehouse,
isdiscontinued: alteredArray[i]["disc"],
backorderdate: alteredArray[i]["etd"],
backorderavailability: alteredArray[i]["boq"]
});
}
var jsonObject = {
login: user,
password: password,
items: jsonObjectArray
};
postOptions.url = endpoint;
postOptions.body = JSON.stringify(jsonObject);
funcArray.push({func:function(postOptions){request(postOptions,(err,res,body)=>{if(err){console.error(err);throw err;}console.log(body);})},vars:postOptions});
jsonObjectArray.length = 0;
}
var mili = 180000;
for(let i = 0;i < funcArray.length; i++){
setTimeout(()=>{
var d = JSON.parse(funcArray[i]['vars'].body);
console.log(d);
console.log('request '+ i);
//funcArray[i]['func'](funcArray[i]['vars']);
}, mili * i);
}
});
});
You would need async/await or Promise to handle async actions in node js.
I am not sure if you have node version which supports Async/await so i have tried a promise based solution.
query.on('end', () => {
connection.release();
writeArray = itemArray.slice(0),
alteredArray = [];
var csv = json2csv({ data: writeArray, fields: fields }),
timestamp = new Date(Date.now());
timestamp = timestamp.getFullYear() + '-' + (timestamp.getMonth() + 1) + '-' + timestamp.getDate() + ' ' + timestamp.getHours() + ':' + timestamp.getMinutes() + ':' + timestamp.getSeconds();
let fpath = './public/assets/archives/opalEdiInventory-' + timestamp + '.csv';
var calls = chunk(itemArray, 1000)
.map(function(chunk) {
var renameditemsArray = chunk.map((item) => new renamedItem(item, warehouse));
var postOptions = {};
postOptions.url = endpoint;
postOptions.body = JSON.stringify({
login: user,
password: password,
items: renameditemsArray
});
return postOptions;
});
sequenceBatch(calls, makeRequest)
.then(function() {
console.log('done');
})
.catch(function(err) {
console.log('failed', err)
});
function sequenceBatch (calls, cb) {
var sequence = Promise.resolve();
var count = 1;
calls.forEach(function (callOptions) {
count++;
sequence = sequence.then(()=> {
return new Promise(function (resolve, reject){
setTimeout(function () {
try {
cb(callOptions);
resolve(`callsequence${count} done`);
}
catch(err) {
reject(`callsequence ${count} failed`);
}
}, 180000);
});
})
});
return sequence;
}
function makeRequest(postOptions) {
request(postOptions, (err, res, body) => {
if (err) {
console.error(err);
throw err;
}
console.log(body)
});
}
function chunk(arr, len) {
var chunks = [],
i = 0,
n = arr.length;
while (i < n) {
chunks.push(arr.slice(i, i += len));
}
return chunks;
}
function renamedItem(item, warehouse) {
this.sku = item['sku']
this.quantity = item["quantity"]
this.overstockquantity = item["osInv"]
this.warehouse = warehouse
this.isdiscontinued = item["disc"]
this.backorderdate = item["etd"]
this.backorderavailability= item["boq"]
}
});
Could you please try this snippet and let me know if it works?I couldn't test it since made it up on the fly. the core logic is in the sequenceBatch function. the The answer is based on an another question which explains how timeouts and promises works together.
Turns out this wasn't a closure or async issues at all, the request object I was building was using references to objects instead of shallow copies resulting in the data all being linked to the same object ref in the ending array.

How to wait for forEach to complete when each iteration calls an asynchronous options?

Alright, here's what the plan is. Go through each file, add the file into the array. Once all files are added, then combine them using the JSZipUtility and Docxtemplater:
'click .merge-icon': (e) => {
var programId = Router.current().url.split('/').pop();
var programObj = Programs.findOne(programId);
var insertedDocuments = [];
var i = 0;
var count = programObj.activityIds.count;
var fileDownloadPromise = new Promise((resolve, reject) => {
programObj.activityIds.forEach(function(activityId) {
var activityObj = Activities.findOne(activityId);
var documentObj = ActivityFiles.findOne(activityObj.documents.pop()._id);
JSZipUtils.getBinaryContent(documentObj.url(), callback);
function callback(error, content) {
var zip = new JSZip(content);
var doc = new Docxtemplater().loadZip(zip);
var xml = zip.files[doc.fileTypeConfig.textPath].asText();
xml = xml.substring(xml.indexOf("<w:body>") + 8);
xml = xml.substring(0, xml.indexOf("</w:body>"));
xml = xml.substring(0, xml.indexOf("<w:sectPr"));
insertedDocuments.push(xml);
i++;
if (i == count - 1) {
resolve();
}
}
});
});
fileDownloadPromise.then(() => {
JSZipUtils.getBinaryContent('/assets/template.docx', callback);
function callback(error, content) {
console.log(content);
var zip = new JSZip(content);
var doc = new Docxtemplater().loadZip(zip);
setData(doc);
}
function setData(doc) {
doc.setData({
body: insertedDocuments.join('<w:br/><w:br/>')
});
doc.render();
useResult(doc);
}
function useResult(doc) {
var out = doc.getZip().generate({
type: 'blob',
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
});
saveAs(out, programObj.name + '.docx');
}
});
}
Turns out nothing's happening. What's wrong with the execution of this Promise here ?
I'm only calling resolve when every file has been loaded in the array.

Updating outside value from inside a promise

I have a getValidUrls function that takes a maxId param.
Within this function it loops backwards and sends a request for the url.
Each loop decrements the maxId.
My issue is that I am trying to add these valid urls to an array, but I cannot update the array from within the .then of the promise. I have added a simple total variable to see if I could increment it and could not.
getValidUrls = (maxId) => {
return new Promise((resolve, reject) => {
let validUrls = [];
let idCounter = maxId;
let total = 0; // test to see if updated from inside (it doesn't)
// while(validUrls.length < 10 && idCounter > 0) {
for(let i = maxId; i > 0; i--){
let newsUrl = `https://hacker-news.firebaseio.com/v0/item/${i}.json?print=pretty`;
//console.log(newsUrl); // this shows all the urls & works
getJSONObject(newsUrl)
.then((story) => {
total++;
console.log(total); // this never gets shown
return getUserObject(story.by);
}).then((user) => {
if(user.karma > 5) {
validUrls.push(story);
if(validUrls.length >= 10) {
resolve(validUrls);
}
}
});
}
});
};
The following returns a json object for the url
getJSONObject = (url) => {
return new Promise((resolve, reject) => {
console.log(url); // this works and shows all urls
https.get(url, (response) => {
response.on('data', (data) => {
console.log(JSON.parse(data)); // This never gets shown
resolve(JSON.parse(data));
}, (err) => reject(err));
}, (err) => reject(err));
});
};

Can't get list filled from a promise and use it in html - aurelia

I can't get the finalList filled to use in my html file, it wil run the code to fill it before the promise all code. I need to use this array in my html document so it has to be a this.variable I am using Aurelia.
activate() {
var repoList = [];
var repos = this.http.fetch({Link to github api})
.then(response => response.json())
.then(repos => this.repos = repos);
var trello = new Trello;
trello.getBoards().then(boardList => this.boards = boardList);
var boards = trello.getBoards();
//add github repo to the associated trello board (works)
Promise.all([boards, repos]).then(function(values) {
var count = 0;
for (var i in values[0]) {
for (var a in values[1]) {
if (values[1][a].hasOwnProperty("name")) {
var repo = values[1][a].name.toLowerCase();
var board = values[0][i]['name'].toLowerCase();
repoList[count] = repo;
count++;
if (repo == board) {
console.log(repo + " " + board)
}
}
}
}
});
//this list is always empty (The problem)
this.finalList = repoList;
this.title = "Trello Boards";
}
Something like this should do it. Hard to decipher what's going on in the for loops.
activate() {
let reposPromise = this.http.fetch({Link to github api})
.then(response => response.json());
let boardsPromise = new Trello().getBoards();
return Promise.all([boardsPromise, reposPromise])
.then(([boards, repos]) => {
this.boards = boards;
this.repos = repos;
this.finalList = [];
for (var i in boards) {
for (var a in repos) {
if (values[1][a].hasOwnProperty("name")) {
var repo = values[1][a].name.toLowerCase();
var board = values[0][i]['name'].toLowerCase();
this.finalList.push(repo);
if (repo == board)
{
console.log(repo + " " + board)
}
}
}
}
});
this.title = "Trello Boards";
}
I believe Your finalList should be set inside the promise handler. Like this.
activate() {
var repoList = [];
//I always use this, and I am not sure what do you mean
//by this.finalList, but still I assume you know what you are doing
//And hence I use this!
var that = this;
var repos = this.http.fetch({Link to github api})
.then(response => response.json())
.then(repos => this.repos = repos);
var trello = new Trello;
trello.getBoards().then(boardList => this.boards = boardList);
var boards = trello.getBoards();
//add github repo to the associated trello board (works)
Promise.all([boards, repos]).then(function(values) {
var count = 0;
for (var i in values[0]) {
for (var a in values[1]) {
if (values[1][a].hasOwnProperty("name"))
{
var repo = values[1][a].name.toLowerCase();
var board = values[0][i]['name'].toLowerCase();
repoList[count] = repo;
count++;
if (repo == board)
{
console.log(repo + " " + board)
};
}
};
};
//I believe when promise resolves. You should set the repoList.
that.finalList = repoList;
that.title = "Trello Boards";
});
}
My question is, do you really wanna set title and finalList to this? Just asking.
Hope this helps!

Categories

Resources