I'm trying to get make the async calls bellow (they are async because of a external API, not my design) to run sequentially, now I managed to have foo be awaited by it's calling function but I'm having trouble awaiting for foo2 because I get the following error on the async line
JS ERROR: SyntaxError: missing ) after argument list
What am I missing?
ps: Also is there a better way to "return" a value from the callback than setting a global variable and accessing it from outside?
foo(nick) {
return new Promise((resolve, reject) async () => {
async_foo(par, [],
(c, res) => {
let par2;
try {
par2 = c.somefun(res);
} catch (e) {
logError(e, `Some error`);
return;
}
let output = await this.foo2(par2);
resolve(output);
});
});
}
foo2(par2) {
return new Promise((resolve, reject) => {
par2.asyncfun(
null, this.callback.bind(this, par2));
});
}
Thank you in advance
I think you're just trying to do too much in one Promise:
async function(nick) {
let res1 = await new Promise((resolve, reject) => {
async_foo(par, [], (c, res) => {
try {
resolve(async_foo_finish(res));
} catch (e) {
reject(e);
}
});
});
return new Promise((resolve, reject) => {
res1.asyncfunc(null, (obj, res) => {
try {
resolve(obj.asyncfun_finish(res));
} catch (e) {
reject(e);
}
});
});
}
foo('something').then(output => {
log('output');
}).catch(e => {
logError(e);
});
It's hard to give good advice, since you're not showing real functions.
One of the main purposes of Promises/async-await is to avoid complicated callback nesting. You should generally break your chain of functions into separate Promises, then await them one after the other.
Related
I have an existing async function:
async doJSONGetRequest(getUrl, accessToken) {
return new Promise(function(resolve, reject) {
const reqHeaders = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
};
console.info('url = ' + getUrl);
request.get({
url: getUrl,
headers: reqHeaders,
}, function(err, response) {
if (err) return reject(err);
try {
// console.debug(`response = ${response.body}`);
const parsed = JSON.parse(response.body);
return resolve(parsed);
} catch (err) {
return reject(err);
}
});
});
}
}
I'm trying to test it with Jasmine(v4).
Of course, I don't want this thing to actually make an HTTP request, so I tried rigging up a spy on the 'request' package's 'get' function in the 'beforeAll' section:
describe('RAPIDAPIService', function() {
beforeAll(async function() {
spyOn(request, 'get')
.and
.callFake(async (parameters) => {
if (parameters.url === 'http://localhost/api/getSomething') {
const rsp = {};
rsp.body = 'good stuff';
return rsp;
} else if (parameters.url === 'http://localhost/api/whoops') {
return new Error('401 not found');
} else {
return null;
}
});
});
it('doJSONGetRequest should run successfully', async () => {
expect(api.doJSONGetRequest).toBeDefined();
const res = await api.doJSONGetRequest('http://localhost/api/getSomething', '12345678');
expect(data).toEqual('good stuff');
});
it('doJSONGetRequest should resolve errors properly', async () => {
expect(api.doJSONGetRequest).toBeDefined();
const res = await api.doJSONGetRequest('http://localhost/api/whoops', '12345678');
const expectedError = new Error('401 not found');
expect(res).toEqual(expectedError);
});
Console log statements seem to indicate that I'm actually getting past / returning something from my "await" calls in the "it" tests. But the spies are actually working / detecting that the url's have been called.
(Note that I'm not including here other tests in the same file that do not make asynchronous calls and ARE working... just so you know that there's no problem accessing the actual "api" library and its functions.)
These two tests keep failing with "Error: Timeout - Async function did not complete within 5000ms". And like I said, it seems like they're not returning back to the tests from their calls to the doJSONGetRequest function.
Any thoughts?
Thanks!
I am thinking the issue is the mocking. request.get seems to take two parameters and I am thinking you need to call the 2nd parameter (callback function) once you are done so the resolve can be called.
Try this:
spyOn(request, 'get')
.and
// add callbackFunction as 2nd argument
.callFake((parameters, callbackFunction) => {
if (parameters.url === 'http://localhost/api/getSomething') {
const rsp = {};
rsp.body = 'good stuff';
callbackFunction(null, rsp);
} else if (parameters.url === 'http://localhost/api/whoops') {
callbackFunction({ error: '401 not found' }, {});
} else {
callbackFunction(null, null);
}
});
Implementing the pattern Dan (#gaearon) demonstrated here, I was just wondering the best way to have the common action work standalone in the following scenario:
const commonAction = () => {
return async (dispatch, getState, api) => {
try {
const response = await api.get('/resource');
dispatch(success('SUCCESS', response.data));
} catch(error) {
dispatch(error('ERROR', error));
throw error; //this is the problem
}
}
const combinedAction = () => {
return async (dispatch, getState, api) => {
try {
await dispatch(commonAction());
const otherResponse = await api.get('/otherResource');
dispatch(success('COMBINED_SUCCESS', otherResponse.data));
} catch(error) {
dispatch(error('COMBINED_ERROR', error));
}
}
The above works in that if either fails, the COMBINED_ERROR will be dispatched, due to the commonAction re-throwing it's error. However, if I wanted to use commonAction by itself, I would get an Unhandled promise rejection error due to the re-throw.
One thought I had was an optional bool isStandAlone = true to determine whether to re-throw or not, i.e.
const commonAction = (isStandAlone = true) => {
return async (dispatch, getState, api) => {
try {
const response = await api.get('/resource');
dispatch(success('SUCCESS', response.data));
} catch(error) {
dispatch(error('ERROR', error));
if(!isStandAlone) {
throw error;
}
}
}
And then in the combinedAction I would just:
await dispatch(commonAction(false));
Is this a code smell? Is there a better and/or different way to approach this?
Should HTTPS functions return asynchronous promises like realtime functions have to?
We haven't been returning in HTTPS functions (just using res.status.send etc), and it looks like firebase/function-samples aren't either. But the documentation is slightly ambiguous https://firebase.google.com/docs/functions/terminate-functions .
This works now in the latest Firebase:
exports.asyncFunction = functions.https.onRequest(async (request, response) => {
const result = await someAsyncFunction();
response.send(result);
});
HTTP functions currently do not respect returned promises - they require a sent result in order to terminate normally. If an HTTP function doesn't send a result, it will time out.
All other types of functions require a returned promise in order to wait for asynchronous work to fully complete.
If you don't have any async work to wait for, you can just return immediately.
These are the three cases outlined in the docs.
After much looking around , this is implementation with a Promise worked for me to return a value from a Google Cloud Function where the function needs to make a third-party asynchronous call :
exports.getSomeAccessToken = functions.https.onCall((data, context) => {
var dataStr = JSON.stringify(data, null, '\t');
console.log('ENTER [getSomeAccessToken], got dataStr: ' + dataStr);
return new Promise((resolve, reject) => {
gateway.clientToken.generate({}, function (err, gatewayResponse) {
var result = {
clientToken: gatewayResponse.clientToken
};
var resultStr = JSON.stringify(result, null, '\t');
console.log("resultStr : " + resultStr);
resolve(result);
});
});
});
Your cloud functions should return"end" with either of the following
res.redirect(), res.send(), or res.end()
What they mean by returning promises, is lets imagine you have a cloud function that updated a node in your realtime database, you would like to complete that work before responding to the HTTP request.
Example code
let RemoveSomething = functions.https.onRequest((req, res) => {
cors(req, res, () => {
// Remove something
DoDatabaseWork()
.then(function (result) {
res.status(200).send();
})
.catch(function (err) {
console.error(err);
res.status(501).send();
});
});
});
Update: Added DoDatabaseWork example.
const DoDatabaseWork = function () {
return new Promise(function (resolve, reject) {
// Remove SomeNode
admin.database().ref('/someNode/').remove()
.then(function (result) {
resolve();
})
.catch(function (err) {
console.error(err);
reject();
});
});
}
I am trying to call a function located in service class,and if that function returns data,one boolean variable sets true. I have 2 class as bollow:student.ts and service.ts:
// student.ts
public ngOnInit() {
this.config.load().then(() => {
this.service.getRecords().then(
function () { console.log("success getRecord");
this.loading = false; },
function () { console.log("failed getRecord");
this.loading = true; });
});
}
//service.ts
public getRecord(id: number): Promise<T> {
return this.getRecordImpl();
}
private getRecordsImpl(): Promise<T[]> {
let url = this.serviceUrl;
return this.http.get(url, this.getRequestOptionsWithToken())
.toPromise()
.then(res => {
this.records = this.extractData<T[]>(res);
for (var i = 0; i < this.records.length; i++) {
var record = this.records[i];
this.onRecord(record);
}
return this.records;
})
.catch(this.handleError);
}
by the now, records from service returns, but this.service.getRecords(); is undefined. and I can't use
.then
for handling succeed and failure actions.
I know that it is not good idea to make it synchronous. but think that being Asynchronous causes getRecords becomes undefined. What is the solution for handling that. I want it runs sequentially. and if service returns any records , variable initialize to false, otherwise it sets to true.
Many thanks for any help and guide.
I think your aproach is not correct, what is the point to make a promise synchronous ? If you really really want to do this I suggest you to dig in the Synchronous programming with es6 generators but usually the job is done much smother.
From your code I see that you are consuming your Promise by attaching .then() in the service. In this way you should create a new Promise.
private getRecordsImpl(): Promise<T[]> {
let url = this.serviceUrl;
return new Promise((resolve, reject) => {
this.http.get(url, this.getRequestOptionsWithToken())
.toPromise()
.then(res => {
this.records = this.extractData<T[]>(res);
for (var i = 0; i < this.records.length; i++) {
var record = this.records[i];
this.onRecord(record);
}
resolve(this.records);
})
.catch(this.handleError);
})
}
And in your code use:
this.service.getRecords().then(
function (records) { console.log("success getRecord");
this.loading = false; },
function (err) { console.log("failed getRecord");
this.loading = true; });
I would like to know why this code using async and await does not work.
// I promisified `github.users.get` which is asynchronous
function getUserData() {
return new Promise(function (resolve, reject) {
github.users.get({}, function (err, res) {
if (err) {
reject(err);
} else {
resolve(res)
}
});
});
}
// Similar to above
function getUserEmails() {
return new Promise(function (resolve, reject) {
github.users.getEmails({}, function (err, res) {
if (err) {
reject(err);
} else {
resolve(res)
}
});
});
}
(async function () {
let github = new GithubAPI({version: '3.0.0'});
github.authenticate({
type: 'oauth',
token: // auth token
});
let userData = await getUserData(); // stuck
let emails = await getUserEmails();
// do something
}());
The code never continues beyond let userData = await getUserData();. It is stuck there.
What am I doing wrong? I am using Meteor 1.3.1.