Javascript Promise Chaining Issues (Ember) - javascript

I'm trying to upload a file to a remote server, get the path for that file on that server, and then save it in a record to my own API. Over half the time, the code crashes because returnedNewFile is undefined (though I've checked console.log(newFile) in the previous .then() function and it's always a promise object. What could be causing this behavior? What could be causing returnedNewFile to sometimes be null/undefined? I'm using Django REST + django-rest-json-api + ember.js 2.6, and this code is in a component's javascript file. Thank you in advance!
store.findRecord('file', folderid).then(function(folder) {
var file = fileList.pop();
var newFile = fm.uploadFile(folder, file.name, file);
return newFile;
}).then(function(returnedNewFile) {
var name = returnedNewFile.get('name');
var path = returnedNewFile.get('path');
var doc = store.createRecord('document', {
name: name,
path: path,
});
console.log("doc: ", doc);
doc.save();
return doc;
}).then(function(doc) {
console.log("hi number three");
var grant = store.createRecord('grant', {
department: department,
number: number,
document: doc,
});
grant.save();
return grant;
}).then(null, function(error) {
console.log("Oops: " + error.message)
});
EDIT: Here's the code for uploadFile() (waterbutler is the name of the file server being used on the external server (link)):
uploadFile(folder, name, contents, options = {}) {
let url = folder.get('links').upload;
options.data = contents;
if (!options.query) {
options.query = {};
}
options.query.name = name;
options.query.kind = 'file';
let p = this._waterbutlerRequest('PUT', url, options);
return p.then(() => this._getNewFileInfo(folder, name));
}
And here's the code for _getNewFileInfo:
/**
* Get the `file` model for a newly created file.
*
* #method _getNewFileInfo
* #private
* #param {file} parentFolder Model for the new file's parent folder.
* #param {String} name Name of the new file.
* #return {Promise} Promise that resolves to the new file's model or
* rejects with an error message.
*/
_getNewFileInfo(parentFolder, name) {
let p = this._reloadModel(parentFolder.get('files'));
return p.then((files) => files.findBy('name', name));
}

Over half the time, the code crashes because returnedNewFile is
undefined (though I've checked console.log(newFile) in the previous
.then() function and it's always a promise object. What could be
causing this behavior?
Promises will resolve other Promise when calling .then(). So while you could return a string from promise 1's function, and you would get that same string in its .then() call, the same is not true if you returned another Promise inside its function. It will instead resolve that second promise to a concrete value before giving a .then result.
So if you called newFile.then(function(newFileResult) { ... then newFileResult would be the same as what you're now getting in the above code as the returnedNewFile argument. Why this library's promises sometimes come back with an undefined value is beyond me.
EDIT: Did not quite understand the comment, so an example. I have a function called getText(str) that asynchronously retrieves that string.
response1 = getText("abc").then(ignoredStr => {
return "def";
});
response2 = getText("abc").then(ignoredStr => {
return getText("def");
});
response1.then(function(str1) {
// "def"
});
response2.then(function(str2) {
// "def"
});
Under a certain understanding of promises, str2 would be a Promise instead of a string; but that's not the case. It sees it got back a Promise, not a "value", and waits for that before chaining the .then() call. Even if getText("def") came back in 3 seconds, it would wait 3 more seconds for the second call before giving us a .then() result.

Related

Return SharePoint 2013 current user using Javascript

I would like to learn how to return a value from one function and use it in another one.
I can show the value in a div or get it in an alert window but I need to use the value in the other function.
<script type="text/javascript">
SP.SOD.executeOrDelayUntilScriptLoaded(testCase,"sp.js");
function showUserInfo(){
var clientContext = SP.ClientContext.get_current();
var user = clientContext.get_web().get_currentUser();
clientContext.load(user);
clientContext.executeQueryAsync(function(){
return user.get_loginName();
},function(sender,args){alert(args.get_message());})
}
function testCase() {
var test = showUserInfo();
alert(test);
}
</script>
If you need to support Internet Explorer
To support Internet Explorer, you should use a callback function similar to what #Amos_MSFT suggested. Below you will find my solution, which is quite similar to the solution posted by #Amos_MSFW but with a few differences as well as comments.
// Execute the testCase() function only after sp.js has loaded and is ready.
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', function() {
textCase();
});
function testCase() {
getLoginName(function(error, success) {
if (error) {
alert(error);
} else {
/*
* The line below is not really necessary, as the result of the
* getLoginName() function is already available in the success
* variable. You can already pass it to other functions, like
* this: alert(success);
*/
var loginName = success;
alert(loginName);
}
});
}
function getLoginName(callback) {
var clientContext = SP.ClientContext.get_current();
var user = clientContext.get_web().get_currentUser();
clientContext.load(user);
clientContext.executeQueryAsync(
function() {
/*
* The first argument of the callback() function is used for the
* error message, so we need to use null as the first argument
* when we want to return the login name as the success message.
*/
callback(null, user.get_loginName());
},
function(_sender, args) {
callback(args.get_message());
}
);
}
If you do not need to support Internet Explorer
I suggest you use a Promise if you don’t need to support Internet Explorer. A Promise is a special type of object that represents an operation that is yet to complete, and they make working with asynchronous operations easy and almost fun. I am not fluent enough to explain them in detail, so I advice you to read the article linked above if you are interested. What I will tell you, though, is that we can use a Promise to make sure the testCase() function stops and waits for the showUserInfo() function to finish, as in waits for the login name to be available.
I have not had the chance to test the code below, but it should work. If not, please let me know. I have also added a few comments in case you are not already familiar with Promises.
const showUserInfo = () => {
return new Promise((resolve, reject) => {
const clientContext = SP.ClientContext.get_current();
const currentUser = clientContext.get_web().get_currentUser();
clientContext.load(currentUser);
clientContext.executeQueryAsync(
() => {
/*
* We resolve the Promise when the query has executed successfully.
* Resolving a Promise means marking it as fullfilled, or complete, and
* returning the current user's login name.
*/
resolve(currentUser.get_loginName());
},
(_sender, args) => {
/*
* If something goes wrong, we reject the Promise. Rejecting means
* marking it as failed, while still returning something. In this
* case, we return the error message.
*/
reject(args.get_message());
}
);
});
}
const testCase = async () => {
/*
* We use the await keyword to halt the code and wait for the showUserInfo()
* function to finish. What really happens is that we wait for the Promise
* in the showUserInfo() function to be marked as settled, whether it is
* fullfilled or rejected, and then assign the result to the test constant.
*/
const test = await showUserInfo();
alert(test);
}
SP.SOD.executeOrDelayUntilScriptLoaded(testCase, 'sp.js');
Function executeQueryAsync is asynchronous. You can use the callback function to get the return value.
Sample code for your reference:
<script type="text/javascript">
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', function(){
var testCase=function(callback){
var clientContext = SP.ClientContext.get_current();
var user = clientContext.get_web().get_currentUser();
clientContext.load(user);
clientContext.executeQueryAsync(function(){
callback(null,user.get_loginName()) ;
},function(sender,args){alert(args.get_message());})
}
testCase(function(error,name){
if (error) {
console.error(error);
return;
}
console.log(name);
})
});
</script>

How to return result of a promise as a string?

I am trying to create a bot for the discord platform that will search a SQL database and return the objects to the chat for people to see.
SQL uses promises and I have been unable to successfully turn the promise it is returning me into something I can return to the chat (a string or array).
This code queries the database:
function spell(name) {
var spellData = sql.get("SELECT * FROM spells WHERE LOWER(name) = '"+ name.toLowerCase() + "'");
spellData.then( value => {
console.log(spellData)
return spellData;
});
}
The table:
CREATE TABLE spells (
`name` VARCHAR(25),
`casting_time` VARCHAR(95),
`components` VARCHAR(215),
`description` VARCHAR(3307),
`duration` VARCHAR(52),
`level` INT,
`range` VARCHAR(28),
`school` VARCHAR(13)
);
I'm using node.js, sqlite, and discord.js.
If you want to return the Promise object to the caller, simply do:
function spell(name) {
return sql.get("SELECT * FROM spells WHERE LOWER(name) = '" + name.toLowerCase() + "'")
}
Then, in your client:
spell('some_name').then(function(result) { console.log(result); })
Or, if you're into awaiting:
let results = await spell('some_name')
console.log(results)
Don't know if you're making use of it or not but "parameterized queries" will guard against SQL injection attacks. Your NPM package of choice should have an adequately managed implementation.
It seems like you may be misunderstanding how promises work. Even after a promise resolves, it's still a promise object. So in your then callback, when you call console.log(spellData) you're just logging the promise object. The data you want is actually passed into the callback as value, so the working code would be
function spell(name) {
var spellData = sql.get("SELECT * FROM spells WHERE LOWER(name) = '"+ name.toLowerCase() + "'");
return spellData.then( value => {
console.log(value) //log the returned value
return value; // returning the value from a then function returns a new promise, so the spell function also returns a promise which you can handle similarly
});
}

How does the promise then works?

I am trying to print all asynchronous calls in a sequence using promise. But i didn't understand whether then implicitly returns a promise or we should explicitly return it.
function fakeAjax(url,cb) {
var fake_responses = {
"file1": "The first text",
"file2": "The middle text",
"file3": "The last text"
};
var randomDelay = (Math.round(Math.random() * 1E4) % 8000) + 1000;
console.log("Requesting: " + url);
setTimeout(function(){
cb(fake_responses[url]);
},randomDelay);
}
function output(text) {
console.log(text);
}
// **************************************
function getFile(file) {
return new Promise((resolve, reject)=>{
fakeAjax(file,resolve);
})
};
let p1= getFile('file1');
let p2= getFile('file2');
let p3= getFile('file3');
p1.then(output) // how does this code work? output doesn't return any promise, but still we can chain it with next .then()
.then( ()=> p2)
.then(output)
.then(()=>p3)
.then(output)
.then(()=>output('complete'))
then returns a promise enabling you to chain the functions that way. It implicitly converts takes the return value of output as the next resolution, and then makes it available in next then. In your case, it will be undefined.
Below is slight modification of your snippet, where I'm showing how then can enable you to create new Promise on the fly based on return value of last promise's resolution.
Here, in output method, after console.log, I have returned the value. Try running the snippet to see it in action.
function fakeAjax (url, cb) {
var fake_responses = {
'file1': 'file2',
'file2': 'file3',
'file3': 'The last text'
}
var randomDelay = 500
console.log('Requesting: ' + url)
setTimeout(function () {
cb(fake_responses[url])
}, randomDelay)
}
function output (text) {
console.log(text)
return text; // return the value
}
// **************************************
function getFile (file) {
console.log('creating prommise for', file)
return new Promise((resolve, reject) => {
fakeAjax(file, resolve)
})
}
var p1 = getFile('file1')
p1.then(output)
.then(getFile)
.then(output)
.then(getFile)
.then(output)
From the documentation:
The then() method returns a Promise. It takes up to two arguments: callback functions for the success and failure cases of the Promise.
If one or both arguments are omitted or are provided non-functions, then then will be missing the handler(s), but will not generate any errors. If the Promise that then is called on adopts a state (fulfillment or rejection) for which then has no handler, a new Promise is created with no additional handlers, simply adopting the final state of the original Promise on which then was called.

How to change global variable value and return the same in javascript

While printing Query, it just printing "Undefined" but i need the value fetching from the excel to be printed.
this.getQuery = function(excelpath, sheetName, queryName) {
var query;
var workbook = new path.Workbook()
workbook.xlsx.readFile(excelpath)
.then(function(){
var sheet = workbook.getWorksheet(sheetName)
var rowsize=sheet.rowCount
for(i=1;i<rowsize;i++){
var Row = sheet.getRow(i)
var qName = Row.getCell(1).value
if(qName==queryName){
query=Row.getCell(2).value
break;
}
}
})
return query;
};
You're not setting the value of query until the callback you supply to then is executed, which will actually be temporally after the return query line. Reading from a file is likely to take non-negligible time, and the method you're using won't block the call stack in the interim, so the subsequent instructions will still be executed in the meantime and your function will run to completion, returning undefined before the callback code even starts.
If you're using an asynchronous method to read the file, you need to write an asynchronous function to use it and return the result - either by returning a Promise or taking and calling a callback.
This is a decent resource to explain how and why: https://www.pluralsight.com/guides/front-end-javascript/introduction-to-asynchronous-javascript
It happens async and query is returned before the promise of readFile(excelpath) is resolved. You can either wait till the Promise is resolved And chain the return statement OR return the promise directly and Resolve in test spec.
Method 1: Return the query value from inside the resolved promise. So in your test spec resolve the Promise for the actual value
this.getQuery = function(excelpath, sheetName, queryName) {
var query;
var workbook = new path.Workbook()
return workbook.xlsx.readFile(excelpath)
.then(function(){
var sheet = workbook.getWorksheet(sheetName)
var rowsize=sheet.rowCount
for(i=1;i<rowsize;i++){
var Row = sheet.getRow(i)
var qName = Row.getCell(1).value
if(qName===queryName){
query=Row.getCell(2).value
return query;
}
}
})
};
Method 2: Chain the line - return query to be trigerred after the readFile() promise is resolved and and the comparison is done.
this.getQuery = function(excelpath, sheetName, queryName) {
var query;
var workbook = new path.Workbook()
workbook.xlsx.readFile(excelpath)
.then(function(){
var sheet = workbook.getWorksheet(sheetName)
var rowsize=sheet.rowCount
for(i=1;i<rowsize;i++){
var Row = sheet.getRow(i)
var qName = Row.getCell(1).value
if(qName===queryName){
query=Row.getCell(2).value
break;
}
}
// Now Chain the logic of returning the value to this then
}).then(function _returnValueOnlyAfterValueAssigned() {
return query;
})
};

Nodejs Bluebird Promises - How to know if all promises are executed

According to this answer, the promise has been created, but the method 'then'( also 'done') will be executed without waiting for the output from subprocess, I need to have a method which is to be called after completely executing all subprocess, how this can be accomplished using bluebird api?
Sample Code
var Promise = require('bluebird')
var exec = require('child_process').exec
// Array with input/output pairs
var data = [
['input1', 'output1'],
['input2', 'output2'],
.....
]
var PROGRAM = 'cat'
Promise.some(data.map(function(v) {
var input = v[0]
var output = v[1]
new Promise(function(yell, cry) {
exec('echo "' + input + '" | ' + PROGRAM, function(err, stdout) {
if(err) return cry(err)
yell(stdout)
})
}).then(function(out) {
if(out !== output) throw new Error('Output did not match!')
})
}),data.length)
.then(function() {
// Send succes to user if all input-output pair matched
}).catch(function() {
// Send failure to the user if any one pair failed to match
})
Here the 'then' function is executed immediately even before the subprocess is completed.
Promise.some() expects an array of promises as its first argument. You are passing the results of data.map() to it, but your callback to data.map() never returns anything so therefore .map() doesn't construct an array of promises and therefore Promise.some() has nothing to wait on so it calls it's .then() handler immediately.
Also, if you're going to wait for all the promises, then you might as well use Promise.all() instead.
This is what I think you want.
Changes:
Switch to Promise.all() since you want to wait for all the promises.
Return the new Promise so .map() will create the array of promises.
Moved the output check into the original promise so it can reject rather than throw and it just seems like that moves all the result checking into one place.
Added all missing semi-colons.
Changed cry and yell to resolve and reject so code would be more familiar to outsiders expecting normal promise names.
Here's the code:
var Promise = require('bluebird');
var exec = require('child_process').exec;
// Array with input/output pairs
var data = [
['input1', 'output1'],
['input2', 'output2']
];
var PROGRAM = 'cat';
Promise.all(data.map(function(v) {
var input = v[0];
var output = v[1];
return new Promise(function(resolve, reject) {
exec('echo "' + input + '" | ' + PROGRAM, function(err, stdout) {
if(err) {
reject(err);
} else if (stdout !== output) {
reject(new Error('Output did not match!'));
} else {
resolve(stdout);
}
});
});
})).then(function() {
// Send succes to user if all input-output pair matched
}).catch(function() {
// Send failure to the user if any one pair failed to match
});

Categories