Functional promise using jQuery - javascript

I am trying to download some data from a Phoenix web app and render the data on the front-end. For this I have the request and callback in a list and am running a list.reduce against it while thening each request.
var Database = function() {
this.reservations = [];
this.applications = [];
this.environments = [];
};
var database = new Database();
var requests = [$.getJSON('/api/applications', data => { database.applications = data }),
$.getJSON('/api/environments', data => { database.environments = data }),
$.getJSON('/api/reservations', data => { database.reservations = data })];
function run() {
requests.reduce(function(chain, callback) {
return (chain ? chain.then(callback) : callback);
}, null).then(() => render(database));
}
However, this works most of the time in latest version of Google Chrome and maybe 10% of the times in Safari.
When I inspect the "database" after stepping into the render function, I see list of applications, but not environments and reservations (2 pieces of data).
Edit: Okay, so it works in Google Chrome in normal mode. But does not work all the time in incognito mode. In Safari it sometimes fetches all 3 pieces of data, and sometimes just 2 pieces of data. My application does not use any sessions.
I am guessing this is caused by the nature of $.ajax being asynchronous and perhaps I broke my promises. But I have hit a roadblock.
Any insights?

You don't need to chain your promises, they can all be requested at the same time since it doesn't look like any of them rely on each other. Instead, you can use a .map() and take the result, which will be a list of promises, and use an .all() to wait for them all to resolve.
(I'm taking some liberties so the following should be considered pseudo-code)
var urls = ['whatever.com', 'example.com/something', 'something.com/whatever'];
var requestPromises = urls.map(function(url) {
return ajax(url); // assume ajax() returns a promise
});
Promise.all(requestPromises).then(function(results) {
console.log(results);
});

Thanks #Soviut for your help. For closure: here's what I did to fix this problem.
// ... is the ES6 spread operator
function run() {
$.when(...requests).then(() => {
render(database);
});
}

Related

Promise inside promise don't wait Axios finishing

I have a trouble. I receive many objects where I mapping them and I make a external consult using axios and save the return, let's the code:
let savedClients = Object.entries(documents).map(personDocument => {
let [person, document] = personDocument
documentFormated = document
documentNumbers = document.replace(/\D/g, '')
return ConsultDocuments.getResponse(documentNumbers).then(resultScore => { // Calling the axios
const info = { ...resultScore }
return Save.saveClient(info)
})
})
Promise.all(savedClients).then(results => {
console.log(results) // Come only one document, repeted with the total documents passed in map
})
The problem is when it realized the all map first and then make the consults with only the last result many time (the total of documents passed)
This code is legacy and use async/await don't work (serious, if i don't stay here)
I'am tried N ways to make this, and with the libary Q(), it's make the map in correcty order but it's doesn't wait the axios, and all results come with "pending"
Thanks!

Electron app performance: blocking thread with ipcRenderer.sendSync

I have a music player built with Electron.
I am having some performance / process blocking that I didn't expect. I am trying to do some background processes for heavy IO operations. (determining songs duration and album covers)
I am doing this by calling methods through the electron.remote module.
I have noticed that the app didn't do these things asynchronously somehow.
I have been running the performance tool to check and saw the click handler taking a huge time.
Digging deeper I found that ipcRenderer.sendSync is called.
There is a warning about sendSync blocking nature in Electron Docs. But, my own code does not call it. So I suspect the remote module or something else in my code causing sendSync to be called.
The entire app code is on Github but here is an example of electron.remote usage.
The gist is something like this:
import {remote} from 'electron'
const fs = remote.require('fs')
const mm = remote.require('musicmetadata')
// read song file, IO
function readMetadata (filePath) {
return new Promise(function (resolve, reject) {
const stream = fs.createReadStream(filePath)
mm(stream, {duration: true}, function (err, metadata) {
// ...
})
})
}
// get metadata for an array of songs
async function refreshSongsDuration (songs) {
const songsMetadata = await Promise.all(songs.map((song) => readMetadata(song.filePath)))
return songs.map((song, index) => {
song.duration = songsMetadata[index].duration
return song
})
}
Then in a click handler I'll have something like this:
playArtist (artistID) {
const songs = this.library.getArtistSongs(artistID)
this.playlist.setSongs(songs)
musicPlayer.play()
const shouldGetDuration = songs.some((song) => song.duration === 0)
// This is expected to be asynchronous and non blocking.
if (shouldGetDuration) {
mediaLibrary.refreshSongsDuration(songs)
.then((updatedSongs) => {
this.playlist.set('songs', updatedSongs)
})
}
}
So, I guess the simple question here is, what am I doing wrong causing these blocking processes?
https://github.com/electron/electron/blob/master/docs/api/remote.md#remote-objects
Each object (including functions) returned by the remote module represents an object in the main process (we call it a remote object or remote function). When you invoke methods of a remote object, call a remote function, or create a new object with the remote constructor (function), you are actually sending synchronous inter-process messages.
every remote module is sync in nature.

How to wait for all dynamic number of forks to complete with redux-saga?

I'm trying to use redux saga to query a number of rest endpoints to get a human readable name associated with each discovered network, to fill a dropdown list with selectable networks.
I'm having trouble doing this, my forking is not working. I keep getting the error message:
TypeError: __webpack_require__.i(..) is not a function(...)
Every example using all() I've found online use call and know ahead of time every request being made. However, judging from the API I tried something like this:
const pendingQueries = [];
for(networkId in discoveredNetworks) {
pendingQueries.push(fork(getApplicationName, networkId);
}
const queryResults = yield all(pendingQueries);
This failed. I've tried a number of other permutations since. From testing I'm able to verify that I can do this:
const results = [];
for(networkId in discoveredNetworks) {
results.push(yield fork(getApplicationName, networkId));
}
and if There is a long enough delay the method will run and complete, though this approach obviously doesn't gaurentee that the forked methods will complete before I use result as I want. Still it seems to confirm the problem is in my use of all.
What is wrong with my all command?
Why don’t you wrap each request in a promise and call them like so:
var promises = []
for(networkId in discoveredNetworks) {
promises.push(new Promise((res, rej) => {
// code from getApplicationName goes here
// call res(result) on success and rej(error) on failure
}));
}
const results = yield call(Promise.all(promises))
I got this to work by giving up on the all() method, which I never got to work as advertised but wasn't really the right method for the job.
For forks I should have been using join() instead. so something along the lines of this:
const pendingQueries = [];
for(networkId in discoveredNetworks) {
pendingQueries.push(yield fork(getApplicationName, networkId);
}
const results = yield join(...pendingQueries);
results.forEach((result) => {
// my logic for handling the response and generating action
}

Stubbing variables in a constructor?

I'm trying to figure out how to properly stub this scenario, but i'm a little stuck.
The scenario is, i've got a db.js file that has a list of couchdb databases in it (each database contains tweet entries for a particular year).
Each year a new database is created and added to this list to hold the new entries for that year (so the list of databases isn't constant, it changes each year).
So my db.js file looks like this:
var nano = require('nano')(`http://${host}`);
var databaseList = {
db1: nano.use('db2012'),
db2: nano.use('db2013'),
db4: nano.use('db2014'),
db5: nano.use('db2015'),
db6: nano.use('db2016')
};
module.exports.connection = nano;
module.exports.databaseList = databaseList;
And event.js (a simple model file), before methods are added looks like this:
var lastInObject = require('../../helpers/last_in_object');
var db = require('../../db');
var EventModel = function EventModel() {
this.connection = db.connection;
this.databaseList = db.databaseList;
this.defaultDatabase = lastInObject(db.databaseList);
};
EventModel.prototype.findAll =
function findAll(db, callback) {/* ... */}
My question is, how do i stub the databaseList, so i can safely test each of the model methods without having any brittleness from the growing databaseList object?
Ideally i'd like to be able hijack the contents of the databaseList in my tests, to mock different scenarios, but i'm unsure how to tackle it.
Here's an example test, to ensure the defaultDatabase property is always pointing to the last known event, but obviously i don't want to have to update this test every year, when databaseList changes, as that makes the tests very brittle.
it('should set the default database to the last known event', () => {
var Event = require('../event');
var newEventModel = new Event();
expect(newEventModel.defaultDatabase.config.db)
.to.equal('db2014');
});
Suggestions welcome! If i've gone about this wrong, let me know what i've done and how i can approach it!
Also, this is just a scenario, i do have tests for lastInObject, i'm more interested in how to mock the concerning data.
In my opinion you need to stub the whole "db" module. That way you won't have any real db connection and you can easily control the environment of your tests. You can achieve this by using the mockery module.
That way you can stub the object that the require('../../db') returns. This will allow you to set whatever value you like in the properties of that object.
Building upon Scotty's comment in the accepted answer a bit... here's some code I've got which seems to do the job. No guarantees that this is a good implementation, but it does successfully stub out the insert and get methods. Hopefully it helps someone. :)
// /src/common/database.js
const database = require('nano')('http://127.0.0.1/database');
module.exports = {
async write(document, documentId) {
return await new Promise(resolve => database.insert(document, documentId, resolve));
},
async read(documentId){
return await new Promise(resolve => database.get(documentId, resolve));
}
};
// /test/setup.js
const proxyquire = require('proxyquire');
proxyquire('../src/common/database.js', {
nano() {
return {
insert(document, documentId, callback){ callback(); },
get(documentId, callback){ callback(); }
};
}
});

Why does one method of stacking promises in Lightswitch work when another doesn't?

I'm working with a Lightswitch 2015 application for tracking customer satisfaction interviews. I'm trying to create a function that will create a new interview, populate a few items, then save it to the database, and open it for editing in a new window. I'm struggling with understanding the behavior of promises in this context.
I have these three blocs of code:
1)
myapp.Interviews_Management.AddInterview_Tap_execute = function (screen)
{
var NewInterview = screen.Interviews.addNew();
NewInterview.c_Date = screen.InterviewDate;
NewInterview.Participant = screen.Interviewee;
NewInterview.Location = screen.InterviewFocusLocation;
screen.closePopup()
.then(() =>
{
myapp.applyChanges()
.then(() =>{ myapp.showInterview_AddEdit(NewInterview); });
}, (error) =>{ msls.showMessageBox(error.toString()); });
};
2)
myapp.Interviews_Management.AddInterview_Tap_execute = function (screen)
{
var NewInterview = screen.Interviews.addNew();
NewInterview.c_Date = screen.InterviewDate;
NewInterview.Participant = screen.Interviewee;
NewInterview.Location = screen.InterviewFocusLocation;
screen.closePopup()
.then(myapp.applyChanges()
.then(myapp.showInterview_AddEdit(NewInterview),
(error) =>{ msls.showMessageBox(error.toString()); })
);
};
3)
myapp.Interviews_Management.AddInterview_Tap_execute = function (screen)
{
var NewInterview = screen.Interviews.addNew();
NewInterview.c_Date = screen.InterviewDate;
NewInterview.Participant = screen.Interviewee;
NewInterview.Location = screen.InterviewFocusLocation;
screen.closePopup()
.then(myapp.applyChanges())
.then(myapp.showInterview_AddEdit(NewInterview),
(error) =>{ msls.showMessageBox(error.toString()); });
};
1 works as expected; that is, creates a new interview, populates the fields, and opens an edit window. However, I'm concerned that errors thrown from inside the lambda function(s) won't be properly passed to the external context.
2 and 3 both create a new interview and populate the fields, but then throws an error saying, "This action cannot be taken while screen is navigating.", which seems to suggest it is not waiting for each promise to fulfill before attempting to execute the next one. 3 also seems like perhaps it is wrapping promises in other promises, which may again lead to not properly passing any thrown errors outward (and, I hear, is a pretty common anti-pattern for people struggling with understanding promises?).
Can someone help me understand what exactly is going on here? I've been struggling generally with proper best practice in nesting promises or using a series of promises; any help with proper way of handling this would be much appreciated!
You always need to pass a callback function to then, not the result of calling it immediately.
You want
screen.closePopup()
.then(myapp.applyChanges) // or () => myapp.applyChanges()
.then(() => myapp.showInterview_AddEdit(NewInterview)),
.catch(error => { msls.showMessageBox(error.toString()); });
For why that catch is necessary (better than using the second then parameter), see When is .then(success, fail) considered an antipattern for promises?.

Categories

Resources