Javascript - Google API GET: Value null outside google api call - javascript

I am making a call to Google Calendar API for a specific event and am able to get the recurrence value.
function getRecurrence(payload) {
console.log('payload = ' + payload);
var recurrence = '';
if (payload != undefined) {
var requestRecurringEvent = window.gapi.client.calendar.events.get({
'calendarId': 'primary',
'eventId': payload
});
requestRecurringEvent.execute(function(resp) {
console.log('requestRecurringEvent = ' + resp);
console.log('requestRecurringEvent.recurrence = ' + resp.recurrence);
recurrence = resp.recurrence;
console.log('recurrence (inside execute)= ' + recurrence); //NO ISSUE (YET): recurrence (inside execute) = RRULE:FREQ=WEEKLY;COUNT=10
return recurrence;
});
} else {
return 'no recurrence value';
}
}
However, when I return the recurrence value to a variable
var recurrence = getRecurrence(action.payload.calendarEventRecurringEventId);
console.log('recurrence (outside execute) = ' + recurrence); //ISSUE: recurrence (outside execute) = undefined
note: action.payload.calendarEventRecurringEventId is because I am passing in the value from payload in redux.
Why is the value defined inside the api call but undefined outside the api call??
Thanks!
UPDATE
Promises seem to have taken me in the right direction, since the console.log('recurrence (outside execute) = ' + recurrence) prints a value, but it seems to not have completely solved the problem since an empty string is still being set for the value in redux.
var recurrencePromise = new Promise(function(resolve, reject) {
var payload = action.payload.calendarEventRecurringEventId;
console.log('action.payload.calendarEventRecurringEventId = ' + payload);
var recurrence = '';
if (payload != undefined) {
console.log('getRecurrence payload != undefined');
var requestRecurringEvent = window.gapi.client.calendar.events.get({
'calendarId': 'primary',
'eventId': payload
});
requestRecurringEvent.execute(function(resp) {
console.log('requestRecurringEvent = ' + resp);
console.log('requestRecurringEvent.recurrence = ' + resp.recurrence);
recurrence = resp.recurrence;
console.log('recurrence (inside execute)= ' + recurrence);
resolve(recurrence);
});
} else {
reject(Error("It broke"));
}
});
recurrencePromise.then(function(recurrence) {
console.log('recurrence (outside execute) = ' + recurrence);
var recurrenceTemp = 'RRULE:FREQ=WEEKLY;COUNT=10';
return {
...state,
calendarEventEndDate: action.payload.calendarEventEndDate,
calendarEventEndDateTime: action.payload.calendarEventEndDateTime,
calendarEventEndTime: action.payload.calendarEventEndTime,
calendarEventID: action.payload.calendarEventID,
calendarEventTitle: action.payload.calendarEventTitle,
calendarEventRecurringEventId: defaultIfUndefined(action.payload.calendarEventRecurringEventId, ''),
calendarEventRecurrence: recurrence, //ISSUE: this is where the value should be something like `RRULE:FREQ=WEEKLY;COUNT=10` instead of `''`
}
}, function(err) {
console.log(err); // Error: "It broke"
});
Why is the right value still not set defined outside the api call?

Because the requestRecurringEvent.execute function is asynchronous, which means you can never tell when the asynchronous function completes its execution (returns) unlike a synchronous function that guaranties a return value, async doesnt. What you need to do if you want to use the recurrence value outside is, A) Use a callback function B) use Promises (which I am sure the google api call provides by default)
I recommend you read more about asynchronity in javascript, to have a grasp on how it works.

Related

Firebase cloud functions: "Unhandled error RangeError: Maximum call stack size exceeded"

I have a cloud function that uses firebase and after I call it from my angular app I get the mentioned above error:
Unhandled error RangeError: Maximum call stack size exceeded
at baseKeys (/workspace/node_modules/lodash/lodash.js:3483:12)
at keys (/workspace/node_modules/lodash/lodash.js:13333:60)
at /workspace/node_modules/lodash/lodash.js:4920:21
at baseForOwn (/workspace/node_modules/lodash/lodash.js:2990:24)
at Function.mapValues (/workspace/node_modules/lodash/lodash.js:13426:7)
at encode (/workspace/node_modules/firebase-functions/lib/providers/https.js:184:18)
at /workspace/node_modules/lodash/lodash.js:13427:38
at /workspace/node_modules/lodash/lodash.js:4925:15
at baseForOwn (/workspace/node_modules/lodash/lodash.js:2990:24)
at Function.mapValues (/workspace/node_modules/lodash/lodash.js:13426:7
I've searched stack to find solutions - but in most cases there was serialization problem, that I believe DOES NOT happen here.
Here is my function:
exports.createCase = functions.region('europe-west2').https.onCall((data, context) => {
console.log("creating new case");
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'This function must be called ' +
'while authenticated.');
}
const caseName = data.caseName;
// Authentication / user information is automatically added to the request.
const uid = context.auth.uid;
const name = context.auth.token.name || null;
const picture = context.auth.token.picture || null;
const email = context.auth.token.email || null;
console.log("caseName=" + caseName + " uid=" + uid + " name=" + name + " picture=" +
picture + " email=" + email);
var operationResult = new Promise ((resolve, reject) => {
var accessData : any = {};
var accessId = admin.database().ref('/access/').push();
var operationId = admin.database().ref('/operationslog/' + accessId.key + '/').push();
console.log("accessId created=" + accessId + ' || ' + accessId.key + ' operations id=' +
operationId + ' || ' + operationId.key);
let now: number = Date.now();
accessData[`/access/` + accessId.key] = new Access(caseName, uid, email);
accessData[`/operationslog/` + accessId.key + `/` + operationId.key] = {
date: now,
performedByUser: uid,
performedByMail: email,
performedByImg: picture,
performedBySystem: false,
operationType: 'CREATE',
order: (REVERSE_ORDER_MAX - now),
details: {creator: uid, name: caseName}
};
console.log('commiting data');
admin.database().ref().update(accessData).then( (value: void) => {
console.log("returning ok result");
resolve({
status: "Ok",
accessId: accessId,
description: 'Case created'
});
}, err => {
console.log("Error while trying to create case: " + err);
reject("CASE NOT CREATED");
}
).catch(exception => {
console.log("Error while trying to create case: " + exception);
reject("CASE NOT CREATED");
}
);
}
);
return operationResult;
});
and the call from Angular app:
let createCaseCall = functions.httpsCallable('createCase');
createCaseCall({caseName: value.caseName}).then(result => {
// Read result of the Cloud Function.
console.log("got result: " + result);
if (result.data.status == 'Ok') {
this.showSuccessMessage('Case created.');
}
}).catch(err => {
console.log("Error while calling cloud functions: " + err);
this.showErrorMessage('Error while creating the case.');
});
Now, the important information is that, the data in firebase realtime database IS CREATED when this function is called and console log does contain "returning ok result" line...
It is still a serialization problem.
Here's what you're trying to send back to the client:
resolve({
status: "Ok",
accessId: accessId,
description: 'Case created'
});
accessId is the result of a push operation:
var accessId = admin.database().ref('/access/').push();
That means it's a DatabaseReference object, which contains circular references that can't be serialized. It's not a simple data type, like a string.
You'll want to think more carefully about what you want exactly you want to send back to the client app. Maybe you wanted to send back the name or path of the child key that was created by push()?
Also, you'll probably want to remove the whole new Promise() thing, as that's an anti-pattern here. There is no need to create a new promise when you have promises from all the other database operations available to work with.

Throw custom timeout exception

I have a Google Apps Script web app ("Web App") that executes as the user, then calls individual functions from another Apps Script project ("API Executable") via the Apps Script API using UrlFetchApp.fetch() and executes them as me (see Get user info when someone runs Google Apps Script web app as me).
A limitation of this method is that UrlFetchApp.fetch() has a 60s timeout, and one of my functions often takes longer than this. The API Executable function finishes running successfully, but the web app throws a timeout exception. I would like to handle this exception by running a second "follow-up" function that finds and returns the URL of the Google Sheet successfully created by the original function. However, I'll need to pass the follow-up function one of the parameters passed to the original function, and it appears I can't do this within a standard try...catch block.
My idea was to throw an exception that contains the needed parameter, but I can't figure out how to throw my own timeout exception; since Google Apps Script is synchronous, there's no way to track how long UrlFetchApp.fetch() has been running while it's running.
Is there a way to throw your own timeout exception? Or, is there another way I can pass the needed parameter to a function that executes if there's a timeout error?
I tagged Javascript in this post as well since there's a lot of overlap with Google Apps Script and I figured it would improve my chance of connecting with someone who has an answer--hope that's okay. Below is the function I'm using in my web app to call my API Executable functions, in case that's helpful.
EDIT: Based on #TheMaster's comment, I decided to write the script as though parameters passed to executeAsMe() WERE being passed to the catch() block, to see what happened. I expected an exception regarding the fact the opt_timeoutFunction was undefined, but strangely it looks like only the first line of the catch() block is even running, and I'm not sure why.
function executeAsMe(functionName, paramsArray, opt_timeoutFunction, opt_timeoutParams) {
try {
console.log('Using Apps Script API to call function ' + functionName.toString() + ' with parameter(s) ' + paramsArray.toString());
var url = 'https://script.googleapis.com/v1/scripts/Mq71nLXJPX95eVDFPW2DJzcB61X_XfA8E:run';
var payload = JSON.stringify({"function": functionName, "parameters": paramsArray, "devMode": true})
var params = {method:"POST",
headers: {Authorization: 'Bearer ' + getAppsScriptService().getAccessToken()},
payload:payload,
contentType:"application/json",
muteHttpExceptions:true};
var results = UrlFetchApp.fetch(url, params);
var jsonResponse = JSON.parse(results).response;
if (jsonResponse == undefined) {
var jsonResults = undefined;
} else {
var jsonResults = jsonResponse.result;
}
} catch(error) {
console.log('error = ' + error); // I'm seeing this in the logs...
console.log('error.indexOf("Timeout") = ' + error.indexOf("Timeout").toString); // ...but not this. It skips straight to the finally block
if (error.indexOf('Timeout') > 0) { // If error is a timeout error, call follow-up function
console.log('Using Apps Script API to call follow-up function ' + opt_timeoutFunction.toString() + ' with parameter(s) ' + paramsArray.toString());
var url = 'https://script.googleapis.com/v1/scripts/Mq71nLXJPX95eVDFPW2DJzcB61X_XfA8E:run';
var payload = JSON.stringify({"function": opt_timeoutFunction, "parameters": opt_timeoutParams, "devMode": true})
var params = {method:"POST",
headers: {Authorization: 'Bearer ' + getAppsScriptService().getAccessToken()},
payload:payload,
contentType:"application/json",
muteHttpExceptions:true};
var results = UrlFetchApp.fetch(url, params);
var jsonResponse = JSON.parse(results).response;
if (jsonResponse == undefined) {
var jsonResults = undefined;
} else {
var jsonResults = jsonResponse.result;
}
}
} finally {
console.log('jsonResults = ' + jsonResults);
return jsonResults;
}
}
I ended up using the '''catch()''' block to throw an exception back to the client side and handle it there.
Google Apps Script:
function executeAsMe(functionName, paramsArray) {
try {
console.log('Using Apps Script API to call function ' + functionName.toString() + ' with parameter(s) ' + paramsArray.toString());
var url = 'https://script.googleapis.com/v1/scripts/Mq71nLXJPX95eVDFPW2DJzcB61X_XfA8E:run';
var payload = JSON.stringify({"function": functionName, "parameters": paramsArray, "devMode": true})
var params = {method:"POST",
headers: {Authorization: 'Bearer ' + getAppsScriptService().getAccessToken()},
payload:payload,
contentType:"application/json",
muteHttpExceptions:true};
var results = UrlFetchApp.fetch(url, params);
var jsonResponse = JSON.parse(results).response;
if (jsonResponse == undefined) {
var jsonResults = undefined;
} else {
var jsonResults = jsonResponse.result;
}
return jsonResults;
} catch(error) {
console.log('error = ' + error);
if (error.toString().indexOf('Timeout') > 0) {
console.log('Throwing new error');
throw new Error('timeout');
} else {
throw new Error('unknown');
}
} finally {
}
}
Client-side Javascript (a simplified version):
function createMcs() {
var userFolder = getDataFromHtml().userFolder;
google.script.run
.withSuccessHandler(createMcsSuccess)
.withFailureHandler(createMcsFailure)
.withUserObject(userFolder)
.executeAsMe('createMasterCombinedSchedule', [userFolder]);
}
function createMcsSuccess(mcsParameter) {
if (mcsParameter == undefined) {
simpleErrorModal.style.display = "block"; // A generic error message
} else {
document.getElementById("simpleAlertHeaderDiv").innerHTML = 'Master Combined Schedule Created Successfully';
document.getElementById("simpleAlertBodyDiv").innerHTML = 'Your Master Combined Schedule was created successfully. Click here to view.';
simpleAlertModal.style.display = "block";
}
}
function createMcsFailure(mcsError, userFolder, counter) { // The exception I threw will activate this function
if (!counter) { // Added a counter to increment every time checkForCreatedMcs() runs so it doesn't run indefinitely
var counter = 0;
}
if (mcsError.message == 'Error: timeout' && counter < 5) { // If timeout error, wait 10s and look for MCS URL
window.setTimeout(checkForCreatedMcs(mcsError, userFolder, counter), 10000);
} else if (mcsError.message == 'Error: timeout' && counter == 5) { // If it hasn't worked after 5 tries, show generic error message
simpleErrorModal.style.display = "block";
} else { // For any error that's not a timeout exception, show generic error message
simpleErrorModal.style.display = "block";
}
}
function checkForCreatedMcs(mcsError, userFolder, counter) {
counter++;
google.script.run
.withSuccessHandler(checkForCreatedMcsSuccess)
.withUserObject([mcsError, userFolder, counter])
.executeAsMe('checkIfMcsExists', [userFolder]); // checkIfMcsExists() is a pre-existing function in my API Executable project I had already used elsewhere
}
function checkForCreatedMcsSuccess(mcsExistsParameter, params) {
var mcsError = params[0];
var userFolder = params[1];
var counter = params[2];
if (mcsExistsParameter == undefined) { // If function returns undefined, show generic error message
simpleErrorModal.style.display = "block";
} else if (mcsExistsParameter == false) { // If function returns false, wait 10s and try again
createMcsFailure(mcsError, userFolder, counter);
} else { // If function returns URL, show success modal with link
document.getElementById("simpleAlertHeaderDiv").innerHTML = 'Master Combined Schedule Created Successfully';
document.getElementById("simpleAlertBodyDiv").innerHTML = 'Your Master Combined Schedule was created successfully. Click here to view.';
simpleAlertModal.style.display = "block";
}
}
I am sure there has to be a tidier/less complex way to do this, but this worked!

I'm Building an auto dialer with Twilio.Device Js

I'm building an auto dialer with twilio and I'm a litle bit stuck . I succed with getting 2 outbound calls from my browser and when the first hangs up or busy the second one starts . but I want to implement it for many calls from an array of phones in my sql Db .
here is my code - I'm starting with connecting to twilio and getting a token after that I'm making an array of phones using an AJAX call to my db with php code , Then I'm making a $GetJson request for the first phone and the next phone (if exist) from the Db and finally I'm starting a twilio.device.connect and when the first disconnect the second's starts:
status_progress.php
/********TWILIO **********/
$.getJSON('../../twilio/token.php').done(function (data) {
console.log('Token: ' + data.token);
console.log('Identity: ' + data.identity);
// Setup Twilio.Device
Twilio.Device.setup(data.token);
Twilio.Device.ready(function (device) {
console.log('Twilio.Device Ready!');
});
Twilio.Device.error(function (error) {
console.log('Twilio.Device Error: ' + error.message);
});
Twilio.Device.connect(function (conn) {
console.log('Successfully established call!');
});
Twilio.Device.disconnect(function (conn) {
console.log('Call ended.');
});
Twilio.Device.incoming(function (conn) {
console.log('Incoming connection from ' + conn.parameters.From);
var archEnemyPhoneNumber = '+12099517118';
if (conn.parameters.From === archEnemyPhoneNumber) {
conn.reject();
console.log('It\'s your nemesis. Rejected call.');
} else {
// accept the incoming connection and start two-way audio
conn.accept();
}
});
//setClientNameUI(data.identity);
})
.fail(function () {
console.log('Could not get a token from server!');
});
// Bind button to hangup call
document.getElementById('closeCall').onclick = function () {
console.log('Hanging up...');
Twilio.Device.disconnectAll();
};
$("#phoneThem").live("click", function() {
var arr = [];
var i =0;
$('.checkDeal:checked').each(function () {
arr[i++] = $(this).val();
});
var arr_value = JSON.stringify(arr);
var conected = 'no';
$.ajax({
type: "POST",
url: 'make_call_list.php',
data: {arr_checked : arr_value}, // serializes the form's elements.
success: function(calls)
{
$.getJSON('make_a_call.php').done(function (data) {
var arr = data.arr;
var params = {
To: data.phone + '-' + data.crm
};
console.log('Calling ' + params.To + '...' );
var connection = Twilio.Device.connect(params);
connection.on('disconnect', function(conn) {
console.log("the call has ended");
var params2 = {
To: data.phone_next + '-' + data.crm
};
console.log('Calling ' + params2.To + '...' );
var connection_2 = Twilio.Device.connect(params2);
});
});
}, // SUCCES MAKE A CALL LIST
});
});
this is the twiml code :
<?php
require_once 'twilio-php-master/Twilio/autoload.php';
use Twilio\TwiML\VoiceResponse;
$break = $_POST['To'];
$break_arr = explode('-',$break);
$prefix = '+972';
$response = new VoiceResponse();
$dial = $response->dial('', ['callerId' => '+972 50-225-8234']);
$break_arr[0] = ltrim($break_arr[0] , '0');
$break_arr[0] = $prefix.$break_arr[0] ;
$dial->number($break_arr[0], ['statusCallbackEvent' => 'initiated
ringing answered completed',
'statusCallback' =>
'https://crm.unic.co.il/crm_test_zone/handleDialCallStatus.php?
crm='.$break_arr[1].'',
'statusCallbackMethod' => 'POST']);
echo $response;
?>
Twilio developer evangelist here.
Rather than write out the code to sequentially dial all of the numbers, you can use the callback functions within the Twilio Device to trigger the next call to happen.
What I would do is load all of the numbers you want to dial into the front end in an array.
Then, I would create a function that takes two arguments; the current index of the number you are dialling and the array of numbers. The function would check to make sure the current index is still within the array and if it isn't return (all numbers have been dialled). Otherwise, it would generate a call to the number in the array. It would also set up a listener for the disconnection event for that call. When it disconnects, it calls the same function again, but moves the index on 1.
Finally, call that function with the initial index of 0 and the array of numbers. Something a bit like this:
const array = getArrayOfNumbers();
function dialNumber(index, array) {
if (index > array.length) return;
const connection = Twilio.Device.connect({ number: array[index] });
connection.on('disconnected', function() {
dialNumber(index+1, array);
});
}
dialNumber(0, array);
Let me know if that helps at all.

AWS S3 / Javascript callback issue

So, I'm having a problem with JavaScript asynchronous execution when making an API call to AWS S3.
I have a sequence of nested callbacks that are working fine up until a specific S3 call that my code is not waiting for. Here's my code:
getThumbUrls(contentIndex, function(data) {
console.log('Returning from getThumbUrls');
// let's just display thumbUrls[0] for now...
console.log('The thumbUrls are ' + data[0]);
});
getThumbUrls() looks like this:
function getThumbUrls(contentIndex, callback) {
console.log('Entering getThumbUrls');
var thumbUrls = [];
JSON.parse(contentIndex).forEach(videoKey => {
// get the thumbnail: bucket-name/thumbnails/<first-key>
console.log('videoKey = ' + videoKey);
getThumbFileName(videoKey, function(thumbFileName) {
console.log('Returning from getThumbFileName');
console.log('Returned thumb filename is ' + thumbFileName);
thumbUrls.push(CLOUDFRONT_URL + videoKey + '/thumbnails/' + thumbFileName);
});
});
callback(thumbUrls);
}
And getThumbFileName() looks like this:
function getThumbFileName(videoKey, callback) {
console.log('Entering getThumbFileName...');
const s3 = new AWS.S3({
apiVersion: '2006-03-01',
params: {
Bucket: 'my-bucket-name'
}
});
// Get the name of the file.
params = {
Bucket: 'my-bucket-name',
Delimiter: '/',
Prefix: videoKey + '/' + THUMBS_FOLDER,
MaxKeys: 1
};
var urlKey;
//console.log('listObjects params = ' + JSON.stringify(params, null, 4));
s3.listObjectsV2(params, (err, data) => {
if (err) {
console.log(err, err.stack);
callback(err);
return;
}
var thumbsKey = data.Contents;
// MaxKeys was 1 bc first thumbnail key is good enough for now. Therefore, only one iteration.
thumbsKey.forEach(function (keys) {
console.log('thumbKey = ' + keys.Key);
urlKey = keys.Key;
});
});
callback(urlKey);
//callback('20161111-TheWind.jpg');
}
Obviously, what's happening is that execution doesn't wait for the s3.listObjectsV2 call to finish. I've verified that the entire flow works properly when all getThumbFileName() does is callback with the filename.
Would someone kindly show me how to force execution to wait for s3.listObjectsV2 to complete before calling back with undefined?
As discussed, you should avoid callbacks approach when dealing with asynchronous operations over iterations, due their difficulty.
(You can skip this section if you don't want to know motivation behind promises approach).
Just to mention, in a callback approach, you must have to wait for all callbacks to complete in your getThumbUrls(), using a if which will check if all callbacks has been called, then just call callback(thumbUrls); with all responses pushed into your thumbUrls array:
function getThumbUrls(contentIndex, callback) {
const thumbUrls = [];
// counter which will increment by one for every callback
let counter = 0;
JSON.parse(contentIndex).forEach(videoKey => {
getThumbFileName(videoKey, function (thumbFileName) {
thumbUrls.push(CLOUDFRONT_URL + videoKey + '/thumbnails/' + thumbFileName);
// for each callback response you must add 1 to a counter and then
counter++;
// check if all callbacks already has been called
if (counter === JSON.parse(contentIndex).length) {
// right here, thumbsUrls are filled with all responses
callback(thumbUrls);
}
});
});
}
So, you can make use of Promises, and a Promise.all will be enough for you to handle all responses from api. You can study over internet and check your code below, which is using a promise approach. I've added some comments to help you understanding what is happening.
// when using promises, no callbacks is needed
getThumbUrls(contentIndex)
.then(function (data) {
console.log('Returning from getThumbUrls');
// let's just display thumbUrls[0] for now...
console.log('The thumbUrls are ' + data[0]);
})
// when using promises, no callbacks is needed
function getThumbUrls(contentIndex) {
console.log('Entering getThumbUrls');
// not needed anymore, Promise.all will return all values
// var thumbUrls = [];
// Promise.all receives an array of promises and returns to next .then() all results
// changing forEach to map to return promises to my Promise.all
return Promise.all(JSON.parse(contentIndex).map(videoKey => {
console.log('videoKey = ' + videoKey);
// returning a promise
return getThumbFileName(videoKey)
.then(function (thumbFileName) {
console.log('Returning from getThumbFileName');
console.log('Returned thumb filename is ' + thumbFileName);
return CLOUDFRONT_URL + videoKey + '/thumbnails/' + thumbFileName;
});
}))
}
// when using promises, no callbacks is needed
function getThumbFileName(videoKey) {
console.log('Entering getThumbFileName...');
const s3 = new AWS.S3({
apiVersion: '2006-03-01',
params: {
Bucket: 'my-bucket-name'
}
});
// Get the name of the file.
params = {
Bucket: 'my-bucket-name',
Delimiter: '/',
Prefix: videoKey + '/' + THUMBS_FOLDER,
MaxKeys: 1
};
// urlKey not need anymore
// var urlKey;
// most of AWS functions has a .promise() method which returns a promise instead calling callback funcions
return s3.listObjectsV2(params).promise()
.then(function (data) {
var thumbsKey = data.Contents;
//if you want to return only first one thumbsKey:
return thumbsKey[0];
})
.catch(function (err) {
console.log(err, err.stack);
callback(err);
return;
})
}
Hope this helps you out in your study.
Would someone kindly show me how to force execution to wait
That's the wrong question. You are not trying to get execution to "wait," or, at least, you shouldn't be. You just need to call the callback in the right place -- inside the callback from s3.listObjectsV2(), not outside.
function getThumbFileName(videoKey, callback) {
...
s3.listObjectsV2(params, (err, data) => {
if (err) {
...
}
var thumbsKey = data.Contents;
// MaxKeys was 1 bc first thumbnail key is good enough for now. Therefore, only one iteration.
thumbsKey.forEach(function (keys) {
console.log('thumbKey = ' + keys.Key);
urlKey = keys.Key;
});
callback(urlKey); // right
});
// wrong // callback(urlKey);
}
The way you wrote it, the callback fires after s3.getObjectsV2() begins to run -- not after it finishes (calls its own callback).

JavaScript async returns 'then not defined'

Am new to async programming, have read similar threads which all seem to have the same problem of not returning anything, whereas mine does...?
However I am still getting the error message 'Cannot read property 'then' of undefined'
function getWorkItems(iterationPath, projectId) {
var queryClient = VSS_Service.getCollectionClient(TFS_Wit_QueryAPI.WorkItemTrackingHttpClient);
var query = { query: "SELECT [System.Id] FROM WorkItem WHERE [System.IterationPath] = '" + iterationPath + "'" };
var resultOfQuery;
queryClient.queryByWiql(query, projectId).then(
function (resultOfQuery) {
return new Promise((resolve, reject) => {
resolve(resultOfQuery);
console.log("Debug: " + JSON.stringify(resultOfQuery));
})
VSS.notifyLoadSucceeded();
});
}
The above prints the debug message fine, it gets the data from the server, then errors with this when I call it elsewhere
let bar;
getWorkItems(counter.path, projectId).then ( res => {
bar = res;
console.log("Debug: should be output of query " + JSON.stringify(bar));
})
Cannot read property 'then' of undefined
getWorkItems doesn't have a return statement in it, so it's implicitly returning undefined. Also, since queryClient.queryByWiql(query, projectId) is already returning a promise, you don't need to create a promise of your own and can do the following:
function getWorkItems(iterationPath, projectId)
{
var queryClient = VSS_Service.getCollectionClient(TFS_Wit_QueryAPI.WorkItemTrackingHttpClient);
var query = { query: "SELECT [System.Id] FROM WorkItem WHERE [System.IterationPath] = '" + iterationPath + "'" };
return queryClient.queryByWiql(query, projectId);
}
If you wanted to keep that log statement and notifyLoadSucceeded in there, you can, but you still don't need to create your own promise. If you're in a .then callback, you just need to return the value that you want the promise to resolve as:
function getWorkItems(iterationPath, projectId)
{
var queryClient = VSS_Service.getCollectionClient(TFS_Wit_QueryAPI.WorkItemTrackingHttpClient);
var query = { query: "SELECT [System.Id] FROM WorkItem WHERE [System.IterationPath] = '" + iterationPath + "'" };
return queryClient.queryByWiql(query, projectId)
.then(function(resultOfQuery) {
console.log("Debug: " + JSON.stringify(resultOfQuery));
VSS.notifyLoadSucceeded();
return resultOfQuery;
});
}

Categories

Resources