React Native Apollo client always return the first fetch result in onComplete - react-native

I'm trying to do a pagination with fetchMore function which is provided by the client it self.
const [getNewReleases, { loading, fetchMore,data:responseData }] = useLazyQuery(FIND_COMPANY)
The first time the getNewReleases called with page = 1 when user scroll to bottom, fetchMore is called with page = 2.
I handle the data using onCompleted, but the result is always the response of page = 1 eventhough the request for page = 2 is called ( I checked in the net work and I see the response)
This is the config in the cache
keyArgs: false,
merge(existing = [], incoming) {
let { result } = incoming
// return result || []
if (result) {
console.log('===== merged Data: ',[...result.data]);
// return [...existing,...result.data]; this doesn't work
return [...result.data] //this not work too
}
else return existing
}
trying to do some log here, I can see that whenever fetchMore is called, it's run to this merger function twice, but return only the request with page = 1.
Is there any fix for this?
Here's the response that I got: https://i.stack.imgur.com/pM1fW.png

Related

Angularjs - what are the possible reasons for duplicate records being inserted by the following code?

The following code is called on the click of a button
$scope.someFunction = function () {
$scope.submitting = true; // the button is disabled if submitting is true
var query = { query: { id: $scope.employeeID } };
// this api call inserts a record in a table
httpFactory.patch("/someURL", query).then(function (data) {
$scope.submitting = false;
if (data.error) {
// display error message
}
else {
// display success message
}
$scope.submitting = false;
}, function () {
$scope.submitting = false;
});
};
can duplicate records be inserted from the call above if a user has poor connectivity or if the server is slow and the request is not completed and soon another same request is received?
If so.. could any one please suggest a suitable way to handle this?

DataTables: How to set page in preInit?

The docs say that I should be able to set the page via DataTablesApiInstance.page(pageNumber), but I can't get it to work.
All the other API methods like search and order seem to work fine.
Here's my code:
$(document)
.on('preInit.dt', (ev, settings) => {
let tableId = ev.target.id;
let tableState = _.get(['datatables', tableId], history.state) || {};
let api = new $.fn.dataTable.Api(settings);
if(tableState.hasOwnProperty('page')) {
api.page(tableState.page); // <-- problem is here; page doesn't get set
}
if(tableState.hasOwnProperty('search')) {
api.search(tableState.search);
}
if(tableState.hasOwnProperty('order')) {
api.order(tableState.order);
}
const setState = (key, value) => {
history.replaceState(_.set(['datatables', tableId, key], value, history.state), '');
};
api.on('page', ev => {
let info = api.page.info();
// console.log('page', tableId, info.page);
setState('page', info.page);
});
api.on('order', ev => {
let order = api.order();
// console.log('order', tableId, order);
setState('order', order);
});
api.on('search', ev => {
setState('search', api.search());
});
});
The method is hit, but the page isn't set. Am I using the wrong API method? Is there another way to set the page before the data loads?
I'm using datatables.net#1.10.12.
If I defer the call to init instead of preInit then the correct page number is highlighted, but the data is still from the first page. If I add a 0ms delay on top of that (as below), it does work, but causes a 2nd data fetch + draw.
if(tableState.page) {
api.on('init', ev => {
setTimeout(() => {
api.page(tableState.page).draw('page');
}, 0);
});
}
How can I set the page without incurring a 2nd ajax request?
You can use displayStart option to define the starting point for data display when using DataTables with pagination as recommended by the author of jQuery DataTables.
It works correctly with table in server-side processing mode and only 1 Ajax request is performed.
var table = $("#example").DataTable({
"processing": true,
"serverSide": true,
"ajax": "/test",
"displayStart": 200
});
From the documentation:
Note that this parameter is the number of records (counting from 0), rather than the page number, so if you have 10 records per page and want to start on the third page, it should be 20 rather than 2 or 3.
This doesn't (directly) answer the question of how to set the page in the preInit, but it solves my problem.
We can use the stateLoadCallback to load the state (including page) from the history API instead of using localStorage as the default implementation does (which will remember the state even when navigating away and then back again).
$.extend(true, $.fn.dataTable.defaults, {
stateSave: true,
stateSaveCallback: (settings, data) => {
let tableId = settings.nTable.id;
if(!tableId) {
// console.warn(`DataTable is missing an ID; cannot save its state`);
return;
}
history.replaceState(_.set(['datatables', tableId], data, history.state), '');
},
stateLoadCallback: settings => {
let tableId = settings.nTable.id;
if(!tableId) {
console.warn(`DataTable is missing an ID; cannot load its state`);
return;
}
return _.get(['datatables', tableId], history.state) || null;
}
});

Angular2 - Multiple dependent sequential http api calls

I am building an Angular2 app and one of the components needs to make multiple API calls which are dependent on the previous ones.
I currently have a service which makes an API call to get a list of TV shows. For each show, I then need to call a different API multiple times to step through the structure to determine if the show exists on a Plex server.
The API documentation is here
For each show, I need to make the following calls and get the correct data to determine if it exists: (Assume we have variables <TVShow>, <Season>, <Episode>)
http://baseURL/library/sections/?X-Plex-Token=xyz will tell me:
title="TV Shows" key="2"
http://baseURL/library/sections/2/all?X-Plex-Token=xyz&title=<TVShow> will tell me: key="/library/metadata/2622/children"
http://baseURL/library/metadata/2622/children?X-Plex-Token=xyz will tell me: index="<Season>" key="/library/metadata/14365/children"
http://baseURL/library/metadata/14365/children?X-Plex-Token=xyz will tell me: index="<Episode>" which implies that the episode I have exists.
The responses are in json, I have removed a lot of the excess text. At each stage I need to check that the right fields exist (<TVShow>, <Season>, <Episode>) so that they can be used for the next call. If not, I need to return that the show does not exist. If it does, I will probably want to return an id for the show.
I have looked at lots of examples including promise, async & flatmap, but am not sure how to solve this based on the other examples I have seen.
How to chain Http calls in Angular2
Angular 2.0 And Http
Angular 2 - What to do when an Http request depends on result of another Http request
Angular 2 chained Http Get Requests with Iterable Array
nodejs async: multiple dependant HTTP API calls
How to gather the result of Web APIs on nodeJS with 'request' and 'async'
Here is what I have for getting the list of shows. (shows.service.ts)
export class ShowsHttpService {
getShows(): Observable<Show[]> {
let shows$ = this._http
.get(this._showHistoryUrl)
.map(mapShows)
.catch(this.handleError);
return shows$;
}
}
function mapShows(response:Response): Show[] {
return response.json().data.map(toShow);
}
function toShow(r:any): Show {
let show = <Show>({
episode: r.episode,
show_name: r.show_name,
season: r.season,
available : false, // I need to fill in this variable if the show is available when querying the Plex API mentioned above.
});
// My best guess is here would be the right spot to call the Plex API as we are dealing with a single show at a time at this point, but I cannot see how.
return show;
}
Here is the relevant code from the component (shows.component.ts)
public getShows():any {
this._ShowsHttpService
.getShows()
.subscribe(w => this.shows = w);
console.log(this.shows);
}
Bonus points
Here are the obvious next questions that are interesting, but not necessary:
The first API query will be much faster than waiting for all of the other queries to take place (4 queries * ~10 shows). Can the initial list be returned and then updated with the available status when it is ready.
The first Plex call to get the key="2" only needs to be performed once. It could be hard coded, but instead, can it be performmed once and remembered?
Is there a way to reduce the number of API calls? I can see that I could remove the show filter, and search through the results on the client, but this doesn't seam ideal either.
The 4 calls for each show must be done sequentially, but each show can be queried in parallel for speed. Is this achievable?
Any thoughts would be much appreciated!
Not sure if I totally understand your question, but here is what I do:
I make the first http call, then when the subscribe fires, it calls completeLogin. I could then fire another http call with its own complete function and repeat the chain.
Here is the component code. The user has filled in the login information and pressed login:
onSubmit() {
console.log(' in on submit');
this.localUser.email = this.loginForm.controls["email"].value;
this.localUser.password = this.loginForm.controls["password"].value;
this.loginMessage = "";
this.checkUserValidation();
}
checkUserValidation() {
this.loginService.getLoggedIn()
.subscribe(loggedIn => {
console.log("in logged in user validation")
if(loggedIn.error != null || loggedIn.error != undefined || loggedIn.error != "") {
this.loginMessage = loggedIn.error;
}
});
this.loginService.validateUser(this.localUser);
}
This calls the loginservice ValidateUser method
validateUser(localUser: LocalUser) {
this.errorMessage = "";
this.email.email = localUser.email;
var parm = "validate~~~" + localUser.email + "/"
var creds = JSON.stringify(this.email);
var headers = new Headers();
headers.append("content-type", this.constants.jsonContentType);
console.log("making call to validate");
this.http.post(this.constants.taskLocalUrl + parm, { headers: headers })
.map((response: Response) => {
console.log("json = " + response.json());
var res = response.json();
var result = <AdminResponseObject>response.json();
console.log(" result: " + result);
return result;
})
.subscribe(
aro => {
this.aro = aro
},
error => {
console.log("in error");
var errorObject = JSON.parse(error._body);
this.errorMessage = errorObject.error_description;
console.log(this.errorMessage);
},
() => this.completeValidateUser(localUser));
console.log("done with post");
}
completeValidateUser(localUser: LocalUser) {
if (this.aro != undefined) {
if (this.aro.errorMessage != null && this.aro.errorMessage != "") {
console.log("aro err " + this.aro.errorMessage);
this.setLoggedIn({ email: localUser.email, password: localUser.password, error: this.aro.errorMessage });
} else {
console.log("log in user");
this.loginUser(localUser);
}
} else {
this.router.navigate(['/verify']);
}
}
In my login service I make a call to the authorization service which returns an observable of token.
loginUser(localUser: LocalUser) {
this.auth.loginUser(localUser)
.subscribe(
token => {
console.log('token = ' + token)
this.token = token
},
error => {
var errorObject = JSON.parse(error._body);
this.errorMessage = errorObject.error_description;
console.log(this.errorMessage);
this.setLoggedIn({ email: "", password: "", error: this.errorMessage });
},
() => this.completeLogin(localUser));
}
In the authorization service:
loginUser(localUser: LocalUser): Observable<Token> {
var email = localUser.email;
var password = localUser.password;
var headers = new Headers();
headers.append("content-type", this.constants.formEncodedContentType);
var creds:string = this.constants.grantString + email + this.constants.passwordString + password;
return this.http.post(this.constants.tokenLocalUrl, creds, { headers: headers })
.map(res => res.json())
}
The point here in this code, is to first call the validateUser method of the login service, upon response, based on the return information, if its valid, I call the loginUser method on the login service. This chain could continue as long as you need it to. You can set class level variables to hold the information that you need in each method of the chain to make decisions on what to do next.
Notice also that you can subscribe to the return in the service and process it there, it doesn't have to return to the component.
Okay, Here goes:
public getShows():any {
this._ShowsHttpService
.getShows()
.subscribe(
w => this.shows = w,
error => this.errorMessage = error,
() => this.completeGetShows());
}
completeGetShow() {
//any logic here to deal with previous get;
this.http.get#2()
.subscribe(
w => this.??? = w),
error => this.error = error,
() => this.completeGet#2);
}
completeGet#2() {
//any logic here to deal with previous get;
this.http.get#3()
.subscribe(
w => this.??? = w),
error => this.error = error,
() => this.completeGet#3);
}
completeGet#3() {
//any logic here to deal with previous get;
//another http: call like above to infinity....
}

YouTube Analytics API: How to enter key | Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup

I am using this sample code for my YouTube analytics API:
(function() {
// Retrieve your client ID from the {{ Google Cloud Console }} at
// {{ https://cloud.google.com/console }}.
var OAUTH2_CLIENT_ID = 'YOUR_CLIENT_ID';
var OAUTH2_SCOPES = [
'https://www.googleapis.com/auth/yt-analytics.readonly',
'https://www.googleapis.com/auth/youtube.readonly'
];
var ONE_MONTH_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 30;
// Keep track of the currently authenticated user's YouTube channel ID.
var channelId;
// For information about the Google Chart Tools API, see:
// https://developers.google.com/chart/interactive/docs/quick_start
google.load('visualization', '1.0', {'packages': ['corechart']});
// Upon loading, the Google APIs JS client automatically invokes this callback.
// See http://code.google.com/p/google-api-javascript-client/wiki/Authentication
window.onJSClientLoad = function() {
gapi.auth.init(function() {
window.setTimeout(checkAuth, 1);
});
};
// Attempt the immediate OAuth 2.0 client flow as soon as the page loads.
// If the currently logged-in Google Account has previously authorized
// the client specified as the OAUTH2_CLIENT_ID, then the authorization
// succeeds with no user intervention. Otherwise, it fails and the
// user interface that prompts for authorization needs to display.
function checkAuth() {
gapi.auth.authorize({
client_id: OAUTH2_CLIENT_ID,
scope: OAUTH2_SCOPES,
immediate: true
}, handleAuthResult);
}
// Handle the result of a gapi.auth.authorize() call.
function handleAuthResult(authResult) {
if (authResult) {
// Authorization was successful. Hide authorization prompts and show
// content that should be visible after authorization succeeds.
$('.pre-auth').hide();
$('.post-auth').show();
loadAPIClientInterfaces();
} else {
// Authorization was unsuccessful. Show content related to prompting for
// authorization and hide content that should be visible if authorization
// succeeds.
$('.post-auth').hide();
$('.pre-auth').show();
// Make the #login-link clickable. Attempt a non-immediate OAuth 2.0
// client flow. The current function is called when that flow completes.
$('#login-link').click(function() {
gapi.auth.authorize({
client_id: OAUTH2_CLIENT_ID,
scope: OAUTH2_SCOPES,
immediate: false
}, handleAuthResult);
});
}
}
// Load the client interfaces for the YouTube Analytics and Data APIs, which
// are required to use the Google APIs JS client. More info is available at
// http://code.google.com/p/google-api-javascript-client/wiki/GettingStarted#Loading_the_Client
function loadAPIClientInterfaces() {
gapi.client.load('youtube', 'v3', function() {
gapi.client.load('youtubeAnalytics', 'v1', function() {
// After both client interfaces load, use the Data API to request
// information about the authenticated user's channel.
getUserChannel();
});
});
}
// Call the Data API to retrieve information about the currently
// authenticated user's YouTube channel.
function getUserChannel() {
// Also see: https://developers.google.com/youtube/v3/docs/channels/list
var request = gapi.client.youtube.channels.list({
// Setting the "mine" request parameter's value to "true" indicates that
// you want to retrieve the currently authenticated user's channel.
mine: true,
part: 'id,contentDetails'
});
request.execute(function(response) {
if ('error' in response) {
displayMessage(response.error.message);
} else {
// We need the channel's channel ID to make calls to the Analytics API.
// The channel ID value has the form "UCdLFeWKpkLhkguiMZUp8lWA".
channelId = response.items[0].id;
// Retrieve the playlist ID that uniquely identifies the playlist of
// videos uploaded to the authenticated user's channel. This value has
// the form "UUdLFeWKpkLhkguiMZUp8lWA".
var uploadsListId = response.items[0].contentDetails.relatedPlaylists.uploads;
// Use the playlist ID to retrieve the list of uploaded videos.
getPlaylistItems(uploadsListId);
}
});
}
// Call the Data API to retrieve the items in a particular playlist. In this
// example, we are retrieving a playlist of the currently authenticated user's
// uploaded videos. By default, the list returns the most recent videos first.
function getPlaylistItems(listId) {
// See https://developers.google.com/youtube/v3/docs/playlistitems/list
var request = gapi.client.youtube.playlistItems.list({
playlistId: listId,
part: 'snippet'
});
request.execute(function(response) {
if ('error' in response) {
displayMessage(response.error.message);
} else {
if ('items' in response) {
// The jQuery.map() function iterates through all of the items in
// the response and creates a new array that only contains the
// specific property we're looking for: videoId.
var videoIds = $.map(response.items, function(item) {
return item.snippet.resourceId.videoId;
});
// Now that we know the IDs of all the videos in the uploads list,
// we can retrieve information about each video.
getVideoMetadata(videoIds);
} else {
displayMessage('There are no videos in your channel.');
}
}
});
}
// Given an array of video IDs, this function obtains metadata about each
// video and then uses that metadata to display a list of videos.
function getVideoMetadata(videoIds) {
// https://developers.google.com/youtube/v3/docs/videos/list
var request = gapi.client.youtube.videos.list({
// The 'id' property's value is a comma-separated string of video IDs.
id: videoIds.join(','),
part: 'id,snippet,statistics'
});
request.execute(function(response) {
if ('error' in response) {
displayMessage(response.error.message);
} else {
// Get the jQuery wrapper for the #video-list element before starting
// the loop.
var videoList = $('#video-list');
$.each(response.items, function() {
// Exclude videos that do not have any views, since those videos
// will not have any interesting viewcount Analytics data.
if (this.statistics.viewCount == 0) {
return;
}
var title = this.snippet.title;
var videoId = this.id;
// Create a new <li> element that contains an <a> element.
// Set the <a> element's text content to the video's title, and
// add a click handler that will display Analytics data when invoked.
var liElement = $('<li>');
var aElement = $('<a>');
// Setting the href value to '#' ensures that the browser renders the
// <a> element as a clickable link.
aElement.attr('href', '#');
aElement.text(title);
aElement.click(function() {
displayVideoAnalytics(videoId);
});
// Call the jQuery.append() method to add the new <a> element to
// the <li> element, and the <li> element to the parent
// list, which is identified by the 'videoList' variable.
liElement.append(aElement);
videoList.append(liElement);
});
if (videoList.children().length == 0) {
// Display a message if the channel does not have any viewed videos.
displayMessage('Your channel does not have any videos that have been viewed.');
}
}
});
}
// This function requests YouTube Analytics data for a video and displays
// the results in a chart.
function displayVideoAnalytics(videoId) {
if (channelId) {
// To use a different date range, modify the ONE_MONTH_IN_MILLISECONDS
// variable to a different millisecond delta as desired.
var today = new Date();
var lastMonth = new Date(today.getTime() - ONE_MONTH_IN_MILLISECONDS);
var request = gapi.client.youtubeAnalytics.reports.query({
// The start-date and end-date parameters must be YYYY-MM-DD strings.
'start-date': formatDateString(lastMonth),
'end-date': formatDateString(today),
// At this time, you need to explicitly specify channel==channelId.
// See https://developers.google.com/youtube/analytics/v1/#ids
ids: 'channel==' + channelId,
dimensions: 'day',
sort: 'day',
// See https://developers.google.com/youtube/analytics/v1/available_reports
// for details about the different filters and metrics you can request
// if the "dimensions" parameter value is "day".
metrics: 'views',
filters: 'video==' + videoId
});
request.execute(function(response) {
// This function is called regardless of whether the request succeeds.
// The response contains YouTube Analytics data or an error message.
if ('error' in response) {
displayMessage(response.error.message);
} else {
displayChart(videoId, response);
}
});
} else {
// The currently authenticated user's channel ID is not available.
displayMessage('The YouTube channel ID for the current user is not available.');
}
}
// This boilerplate code takes a Date object and returns a YYYY-MM-DD string.
function formatDateString(date) {
var yyyy = date.getFullYear().toString();
var mm = padToTwoCharacters(date.getMonth() + 1);
var dd = padToTwoCharacters(date.getDate());
return yyyy + '-' + mm + '-' + dd;
}
// If number is a single digit, prepend a '0'. Otherwise, return the number
// as a string.
function padToTwoCharacters(number) {
if (number < 10) {
return '0' + number;
} else {
return number.toString();
}
}
// Call the Google Chart Tools API to generate a chart of Analytics data.
function displayChart(videoId, response) {
if ('rows' in response) {
hideMessage();
// The columnHeaders property contains an array of objects representing
// each column's title -- e.g.: [{name:"day"},{name:"views"}]
// We need these column titles as a simple array, so we call jQuery.map()
// to get each element's "name" property and create a new array that only
// contains those values.
var columns = $.map(response.columnHeaders, function(item) {
return item.name;
});
// The google.visualization.arrayToDataTable() function wants an array
// of arrays. The first element is an array of column titles, calculated
// above as "columns". The remaining elements are arrays that each
// represent a row of data. Fortunately, response.rows is already in
// this format, so it can just be concatenated.
// See https://developers.google.com/chart/interactive/docs/datatables_dataviews#arraytodatatable
var chartDataArray = [columns].concat(response.rows);
var chartDataTable = google.visualization.arrayToDataTable(chartDataArray);
var chart = new google.visualization.LineChart(document.getElementById('chart'));
chart.draw(chartDataTable, {
// Additional options can be set if desired as described at:
// https://developers.google.com/chart/interactive/docs/reference#visdraw
title: 'Views per Day of Video ' + videoId
});
} else {
displayMessage('No data available for video ' + videoId);
}
}
// This helper method displays a message on the page.
function displayMessage(message) {
$('#message').text(message).show();
}
// This helper method hides a previously displayed message on the page.
function hideMessage() {
$('#message').hide();
}
})();
When I insert in the client ID, it says this:
Daily Limit for Unauthenticated Use Exceeded. Continued use requires
signup.
I researched this, and it means I have to also put in the API Key. Where do I do this?
Thanks,
Ben
Before
gapi.auth.authorize
put
gapi.client.setApiKey(API_KEY);

Returning value from file read with WinJS for use in page

I currently have an issue with a file read in a Windows 8/WinRT application. I have a simple navigation style app, several pages have access to the same data and I have a data.js file that defines a namespace (Data) with a number of members. One part of the application saves items to a txt file stored in the applications local data folder. But on some of the other pages I need to read this in or check for the existence of an item within the list of previously saved items. To do this I added another method into the data.js file. The trouble is, when I call this method to check for the existence of an item, it doesn't return the value straight away due to the async nature, but the rest of code in the page specific js file still seems to execute before it jumps back into the parsing. This means that the logic to check for an item doesn't seem to work. I have a feeling it's down to my use of either .done or .then but my code is as follows:
DATA.JS
var doesItemExist= function(item_id){
var appFolder = Windows.Storage.ApplicationData.current.localFolder;
//note I've tried this with and without the first "return" statement
return appFolder.getFileAsync(dataFile).then(function (file) {
Windows.Storage.FileIO.readTextAsync(file).done(function (text) {
try {
var json = JSON.parse(text);
if (json) {
for (var i = 0; i < json.items.length; i++) {
var temp_item = json.items[i];
if (temp_item.id === item_id) {
return true;
break;
}
}
} else {
return false;
}
} catch (e) {
return false;
console.log(e);
}
}, function (e) { return false;console.log(e); });
}, function (e) { // error handling
return false;
console.log(e);
});
}
WinJS.Namespace.define("Data", {
doesItemExist: doesItemExist
}); //all of the above is wrapped in a self executing function
Then on Page.js I have the following:
var add = document.getElementById('add');
if (Data.doesItemExist(selected_item.id)) {
add.style.display = 'block';
} else {
add.style.display = 'none';
}
All the variables here are assigned and debugging doesn't produce any errors, control just appears to go back to the if/else statement after it hits the getFileAsync but before it even goes through the for loop. But subsequently it does go in to the for loop but after the if statement has finished. I'm guessing this is down to the async nature of it all, but I'm not sure how to get around it. Any ideas?
thanks
A Promise should work here.
I created a new Navigation app, and added a Data.js file containing the following code:
(function () {
var appData = Windows.Storage.ApplicationData;
function doesItemExist(item_id) {
return new WinJS.Promise(
function (completed, error, progress) {
var exists = false;
appData.current.localFolder.createFileAsync("data.txt", Windows.Storage.CreationCollisionOption.openIfExists).then(
function (file) {
Windows.Storage.FileIO.readTextAsync(file).then(
function (fileContents) {
if (fileContents) {
if (fileContents = "foo!") {
completed(true);
}
else {
completed(false);
}
}
else {
completed(false);
}
}
);
},
function (e) {
error(e);
}
);
}
);
}
WinJS.Namespace.define("Data", {
doesItemExist: doesItemExist
});
})();
Note that I've simplified the code for retrieving and parsing the file, since that's not really relevant to the problem. The important part is that once you've determined whether the item exists, you call completed(exists) which triggers the .then or .done of the Promise you're returning. Note that you'd call error(e) if an exception occurs, as I'm doing if there's an exception from the call to createFileAsync (I use this call rather than getFileAsync when I want to be able to either create a file if it does not exist, or return the existing file if it does, using the openIfExists option).
Then, in Home.js, I added the following code to the ready handler:
var itemExists;
var itemExistsPromise = Data.doesItemExist(42);
itemExistsPromise = itemExistsPromise.then(function (exists) {
itemExists = exists;
var content = document.getElementById("content");
content.innerText = "ItemExists is " + itemExists;
});
itemExistsPromise.done(function () {
var a = 42;
});
var b = 0;
The code above sets the variable itemExistsPromise to the returned promise from the function in Data.js, and then uses an anonymous function in the .then function of the Promise to set the variable itemExists to the Boolean value returned from the doesItemExist Promise, and grabs the <p> tag from Home.html (I added an id so I could get to it from code) and sets its text to indicate whether the item exists or not). Because I'm calling .then rather than .done, the call returns another promise, which is passed into the itemExistsPromise variable.
Next, I call itemExistsPromise.done to do any work that has to wait until after the work performed in the .then above it.
If you set a breakpoint on the lines "var a = 42" and "var b = 0" (only included for the purpose of setting breakpoints) as well as on the line "itemExists = exists", you should find that this gives you the control you need over when the various parts are executed.
Hope that helps!