Making a function chainable with then() - javascript

I have a function that uses fetch() to get a value from a database, which is then used to update an element on my page:
function AjaxUpdate(_element, _url, _form_data) {
if (!_element) return; // Nothing to update.
if (concurrency++ == 0)
LoadStatus(_form_data === undefined ? "Loading..." : "Processing...");
fetch(_url, {
method: _form_data === undefined ? "GET" : "POST",
body: _form_data,
})
.then((response) => response.text())
.then((text) => {
if (_element.nodeName == "INPUT") _element.value = text;
else _element.innerHTML = text;
/* inserted */ return new Promise(function (resolve, reject) {
resolve(text);
});
})
.catch((error) => alert(error.message))
.finally(() => {
if (--concurrency == 0) LoadStatus("");
});
}
I like this function to be chainable, by calling it like this:
const company_form = document.getElementById("company_form");
const company_properties = document.getElementById("company_properties");
const cmc_info = document.getElementById("cmc_info");
AjaxUpdate(company_properties, "company?action=edit&CompanyID=12345", new FormData(company_form))
.then(text => { AjaxUpdate(cmc_info, "company?action=editcmc&CompanyID=12345"); });
The initial call is a POST (updates the database) and the second call should wait for the first to complete before starting. To do so I inserted a return new Promise() statement, but that does not work as expected. The initial function call is executed (database is updated) but then I get an error message "TypeError: function AjaxUpdate(...) is undefined".
The single call version is used many times and runs OK; when I put a then() at the end, it breaks down.
Could anyone give me a push...?

You need to add a return statement to AjaxUpdate for the case where you fetch data. And for consistency, you should return a promise in the if (!_element) case too.
function AjaxUpdate(_element, _url, _form_data) {
if (!_element) return Promise.resolve();
// ^^^^^^^^^^^^^^^^^---- MODIFIED
if (concurrency++ == 0)
LoadStatus(_form_data === undefined ? "Loading..." : "Processing...");
return fetch(_url, {
//^^^^^^---- ADDED
method: _form_data === undefined ? "GET" : "POST",
body: _form_data,
})
.then((response) => response.text())
.then((text) => {
if (_element.nodeName == "INPUT") _element.value = text;
else _element.innerHTML = text;
return text;
})
.catch((error) => alert(error.message))
.finally(() => {
if (--concurrency == 0) LoadStatus("");
});
}

Your return statement is scoped under the callback of the then invocation. So AjaxUpdate is not actually returning that Promise but an implicit undefined.
I suggest you three possible solutions.
Return the fetch promise chain result
function AjaxUpdate(_element, _url, _form_data) {
if (!_element) {
// return a dummy Promise
return Promise.resolve(/* whatever you want as a default*/);
}
// ...
// Returning fetch promise chain
return fetch(...)
.then(...)
.then((text) => {
// ...
return text;
// text will be passed from the next `then` invocation
});
}
Return a whole Promise which resolves whenever you need to
function AjaxUpdate(_element, _url, _form_data) {
return new Promise((resolve, reject) => {
if (!_element) {
resolve(/* Any default value ...*/);
// or even reject();
}
// ...
fetch(...)
.then(...)
.then((text) => {
// ...
resolve(text);
})
.catch(reject)
.finally(...);
});
}
Make AjaxUpdate async so it will return an implicit Promise
async function AjaxUpdate(_element, _url, _form_data) {
if (!_element) {
return;
}
// ...
try {
const response = await fetch(...);
const text = await response.text();
// ...
return text;
} catch (e) {
// ...
}
// Finally
// ...
}
Hope it helps.

Related

Return values from SetTimeout function ( Promise.Then())

In the code below , Values are RETURNED correctly from a queued Promise.then() chain .
CODE:
let cond_1 = true;
let data = 'Data Received....';
let err = 'Error';
var p1 = new Promise(function(resolve,reject){
if(cond_1){
resolve(data);
}else{
reject(err); }})
p1.then((data)=>{console.log(data);return 'Wait....';})
.then((val1)=>{console.log(val1); return 'Finished';})
.then((val2)=>{console.log(val2)})
.catch((err)=>{console.log(err)});
Output :
Data Received....
Wait....
Finished
However, the same RETURNED values from a chained SetTimeout function are returned 'UNDEFINED'.
CODE:
p1.then((data)=>{console.log(data); return 'Wait.....'; })
.then((val1)=>{setTimeout(function(val1){console.log(val1); return 'Finished';},1000)})
.then((val2)=>{setTimeout(function(val2){console.log(val2);},1000)})
.catch((err)=>{console.log(err)});
Output:
Data Received....
undefined
undefined
How to resolve this?
Try taking advantage of Lexicographic nature of Javascript.
Instead of making a function v1,v2 which your functions takes within setTimeout, just use an arrow function. In this way you are using the v1,v2 returned from promise.
Do this
let cond_1 = true;
let data = 'Data Received....';
let err = 'Error';
var p1 = new Promise(function(resolve, reject) {
if (cond_1) {
resolve(data);
} else {
reject(err);
}
})
p1.then((data) => {
console.log(data);
return 'Wait.....';
})
.then((val1) => {
setTimeout(() => {
console.log(val1);
}, 1000);
return 'Finished';
})
.then((val2) => {
return setTimeout(() => {
console.log(val2)
}, 1000)
})
.catch((err) => {
console.log(err)
});
What you did was you created a new variable v1,v2 for your function. You can only use that when you pass value v1,v2 in that function. That function won't use v1,v2 returned from promise as you expect.

Synchronizing in Node JS [duplicate]

When using a simple callback such as in the example below:
test() {
api.on( 'someEvent', function( response ) {
return response;
});
}
How can the function be changed to use async / await? Specifically, assuming 'someEvent' is guaranteed to be called once and only once, I'd like the function test to be an async function which does not return until the callback is executed such as:
async test() {
return await api.on( 'someEvent' );
}
async/await is not magic. An async function is a function that can unwrap Promises for you, so you'll need api.on() to return a Promise for that to work. Something like this:
function apiOn(event) {
return new Promise(resolve => {
api.on(event, response => resolve(response));
});
}
Then
async function test() {
return await apiOn( 'someEvent' ); // await is actually optional here
// you'd return a Promise either way.
}
But that's a lie too, because async functions also return Promises themselves, so you aren't going to actually get the value out of test(), but rather, a Promise for a value, which you can use like so:
async function whatever() {
// snip
const response = await test();
// use response here
// snip
}
It's annoying that there isn't a straightforward solution, and wrapping return new Promise(...) is fugly, but I have found an ok work-around using util.promisify (actually it also kinda does the same wrapping, just looks nicer).
function voidFunction(someArgs, callback) {
api.onActionwhichTakesTime(someMoreArgs, (response_we_need) => {
callback(null, response_we_need);
});
}
The above function does not return anything, yet. We can make it return a Promise of the response passed in callback by doing:
const util = require('util');
const asyncFunction = util.promisify(voidFunction);
Now we can actually await the callback.
async function test() {
return await asyncFunction(args);
}
Some rules when using util.promisify
The callback must be the last argument of the function that is gonna be promisify
The supposed-callback must be in the form (err, res) => {...}
Funny thing is we do not need to ever specifically write what's the callback actually is.
async/await is magic. You can create a function asPromise to handle this kind of situations:
function asPromise(context, callbackFunction, ...args) {
return new Promise((resolve, reject) => {
args.push((err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
if (context) {
callbackFunction.call(context, ...args);
} else {
callbackFunction(...args);
}
});
}
and then use it when you want:
async test() {
return await this.asPromise(this, api.on, 'someEvent');
}
the number of args is variable.
You can achieve this without callbacks , use promise async await instead of callbacks here how I would do this. And also here I have illustrated two methods to handle errors
clickMe = async (value) => {
// begin to wait till the message gets here;
let {message, error} = await getMessage(value);
// if error is not null
if(error)
return console.log('error occured ' + error);
return console.log('message ' + message);
}
getMessage = (value) => {
//returning a promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// if passed value is 1 then it is a success
if(value == 1){
resolve({message: "**success**", error: null});
}else if (value == 2){
resolve({message: null, error: "**error**"});
}
}, 1000);
});
}
clickWithTryCatch = async (value) => {
try{
//since promise reject in getMessage2
let message = await getMessage2(value);
console.log('message is ' + message);
}catch(e){
//catching rejects from the promise
console.log('error captured ' + e);
}
}
getMessage2 = (value) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(value == 1)
resolve('**success**');
else if(value == 2)
reject('**error**');
}, 1000);
});
}
<input type='button' value='click to trigger for a value' onclick='clickMe(1)' />
<br/>
<input type='button' value='click to trigger an error' onclick='clickMe(2)' />
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(1)'/>
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(2)'/>
const getprice = async () => {
return await new Promise((resolve, reject) => {
binance.prices('NEOUSDT', (error, ticker) => {
if (error) {
reject(error)
} else {
resolve(ticker);
}
});
})}
router.get('/binanceapi/price', async function (req, res, next) {
res.send(await binanceAPI.getprice());});

nodejs express Why not return res?

I want to finish if isBookmark is true
Enter the then below and console.log(1); This works, I hope it doesn't work
checkLecture(addLectureInformation)
.then(() => {
return insertLecture(addLectureInformation);
})
.then((succesInsertLecture) => {
if (true) {
return res.status(200).json(succesInsertLecture);
} else {
return Promise.all([1]);
}
})
.then(num => {
console.log(1);
})
Help
You can't skip steps in your promise chain, but you can move the last then so it is not called if you returned a response already:
checkLecture(addLectureInformation)
.then(() => {
return insertLecture(addLectureInformation);
})
.then((succesInsertLecture) => {
// this should be other condition, because this will be true always and "else" is not even needed/run
if (true) {
return res.status(200).json(succesInsertLecture);
} else {
// Moving the last "then" here, so it is not called is you have sent a response already
return Promise.all([Promise.resolve(1)]) // Promise all expects an array of promises
.then(num => {
console.log(1);
})
}
});

Export function with promise, wait for response

I'm calling a function inside a then statement, and that function has to wait for an event to fire, but my initial function is returning undefined almost immediately:
// call.js
const dialogflow = require('./dialogflow')
module.exports = {
receive: functions.https.onRequest((request, response) => {
...
let respondToUser = getUserId
.then((uid) => {
payload.uid = uid
dialogflow.handleIncoming(payload).then((result) => {
console.log(result)
})
})
.then((result) => {
console.log(result)
response.end()
})
...
}
}
// dialogflow.js
module.exports = {
handleIncoming: (payload) => {
...
let df = dialogflow.textRequest(message.message, {
sessionId: payload.from
})
.on('response', (response) => {
return response.result.fulfillment.speech
})
.on('error', (error) => {
return 'That\'s an error on my end. Try again later!'
})
.end()
}
}
The goal is to call dialogflow.handleIncoming(payload) from call.js, wait for it to return some text, and then continue. But no matter how I have structured it, receive just keeps blowing through it and dialogflow.handleIncoming(payload) ends up undefined.
I've tried using a promise on df with no success, and I can't figure out how to make respondToUser wait for a full response from handleIncoming. Everything else is working so I'm only including relevant code.
This is using api.ai (dialogflow), but in cloud functions in Firebase if that helps. Appreciate any help!
Problem is dialogflow.handleIncoming(payload) is not structured for async. Try this:
// dialogflow.js
exports.handleIncoming = (payload) =>
new Promise((resolve, reject) => {
...
let df = dialogflow.textRequest(message.message, {
sessionId: payload.from
})
.on('response', (response) => {
resolve(response.result.fulfillment.speech)
})
.on('error', (error) => {
reject ('That\'s an error on my end. Try again later!')
})
.end()
}
Your receive function isn't waiting for dialogflow.handleIncoming(payload) to complete. The then function that contains it doesn't have a return statement, so it's returning undefined rather than returning the result of dialogflow.handleIncoming (which is what you want).
let respondToUser = getUserId
.then((uid) => {
payload.uid = uid
return dialogflow.handleIncoming(payload)
})
.then((result) => {
console.log(result)
response.end()
})
The next then statement will contain the response from diagflow.handleIncoming.

Using throw in promises

I would like to create a function that returns a promise and if something throws an error within, it returns promise reject.
function promiseFunc(options) {
return new Promise(() => {
return options;
});
}
function myfunc(options) {
return new Promise(() => {
if (!options) throw new Error("missing options");
return promiseFunc(options).then((result) => {
if (result.throwerr) throw new Error("thrown on purpose");
return result.value;
});
});
};
My test as follows:
const myfunc = require("./myfunc");
describe('myfunc', () => {
it('should fail without options', () => {
return myfunc()
.then((result) => { throw new Error(result) }, (err) => {
console.log("test #1 result:", err.message === "missing options");
});
});
it('should fail on options.throwerr', () => {
return myfunc({throwerr: true})
.then((result) => {}, (err) => {
console.log("test #2 result:", err.message === "thrown on purpose");
});
});
it('should return options.value', () => {
return myfunc({value: "some result", throwerr: false})
.then((result) => {
console.log("test #3 result:", result === "some result");
}, (err) => {});
});
});
The first test pass, but the second and third fails.
Log #2 does not even run, so I assumed the "throw on purpose" messes up something, therefore I created test #3, where I don't throw anything, but it still fails.
What am I missing?
Solution:
function promiseFunc(options) {
return new Promise(resolve => {
return resolve(options);
});
}
function myfunc(options) {
return new Promise((resolve, reject) => {
if (!options) throw new Error("missing options");
return promiseFunc(options).then(result => {
if (result.throwerr) throw new Error("thrown on purpose");
return resolve(result.value);
}).catch(err => {
return reject(err);
});
});
};
You forgot to pass a function with resolve and reject parameters, so your promises just don't work.
function promiseFunc(options) {
return new Promise(resolve => { // resolve function
resolve(options)
})
}
module.exports = function myfunc(options) {
return new Promise((resolve, reject) => { // since you may either resolve your promise or reject it, you need two params
if (!options) {
return reject(new Error("missing options"))
}
return promiseFunc(options).then(result => {
if (result.throwerr) {
return reject(new Error("thrown on purpose"))
}
resolve(result.value)
})
})
}
... and the test (mocha)
const assert = require('assert'),
myfunc = require("./myfunc")
describe('myfunc', () => {
it('should fail without options', done => { // mind the callback, promises are always async
myfunc()
.catch(err => {
assert(err.message === "missing options")
done() // <- called here
})
})
it('should fail on options.throwerr', done => {
myfunc({throwerr: true})
.catch(err => {
assert(err.message === "thrown on purpose")
done()
})
})
it('should return options.value', done => {
return myfunc({value: "some result", throwerr: false})
.then(result => {
assert(result === "some result")
done()
})
})
})
I would like to create a function that returns a promise and if something throws an error within, it returns promise reject.
This will do it ...
var q = require('q'); // In recent versions of node q is available by default and this line is not required
function iReturnAPromise(num) {
var def = q.defer();
if (typeof num=== 'number') {
try {
var value = 100 / num;
def.resolve(value);
} catch(e) {
def.reject("oops a division error - maybe you divided by zero");
}
} else {
def.reject("o no its not a number");
}
return def.promise;
}
PS this function was coded freehand and has not been tested - but this will work. Obviously try catch should be used sparingly.
PS I prefer the q library implementation of promise instead of the default node promise library - they take a very different approach. q dispenses with all the wrapping!
using the promise library u wanted ...
function iReturnAPromise(num) {
return new Promise(function(resolve, reject) {
if (typeof num === 'number') {
try {
var value = 100 / num;
resolve(value);
} catch (e) {
reject("oops a division error - maybe you divided by zero");
}
} else {
reject("o no its not a number");
}
})
}
iReturnAPromise(7).then(
function(response) {console.log("success", response)},
function(response) {console.log("failure", response)}
);
// Unexpectedly this is not an error in node 5.6 because div by 0 is not an error operation anymore!
iReturnAPromise(0).then(
function(response) {console.log("success", response)},
function(response) {console.log("failure", response)}
);
iReturnAPromise("fred").then(
function(response) {console.log("success", response)},
function(response) {console.log("failure", response)}
);
you can see why i prefer the q syntax :)

Categories

Resources