NodeJS - loop with nested API calls - javascript

Hello I'm new to NodeJs and am trying to work out the best way to get this chain of events working. I have to do two API calls get all the information I need. The first API call is just a list of IDs, then the second API call I pass the ID to get the rest of the information for each object.
However using the method below, I have no idea when everything is finished. Please can someone help me out.
function getData() {
var options = {
method: 'GET',
uri: 'https://api.call1.com',
qs: {
access_token: _accessToken,
}
};
request(options).then(function(apires){
console.log("complete 1");
var obj = JSON.parse(apires);
obj.data.forEach(function(entry) {
findMore(entry.id)
});
})
}
function findMore(id) {
var options = {
method: 'GET',
uri: 'https://api.call2.com',
qs: {
access_token: _accessToken,
}
};
request(options).then(function(apires){
console.log("complete 2");
var obj = JSON.parse(apires);
})
}

You can make your findMore method return a promise, so you can pass an array of those to Promise.all and handle the .then when all promises have finished.
function getData() {
var options = {
method: 'GET',
uri: 'https://api.call1.com',
qs: {
access_token: _accessToken,
}
};
request(options).then(function(apires){
console.log("complete 1");
var obj = JSON.parse(apires);
var promises = [];
obj.data.forEach(function(entry) {
promises.push(findMore(entry.id));
});
return Promise.all(promises);
})
.then(function (response) {
// Here response is an array with all the responses
// from your calls to findMore
})
}
function findMore(id) {
var options = {
method: 'GET',
uri: 'https://api.call2.com',
qs: {
access_token: _accessToken,
}
};
return request(options);
}

A couple of things to think about:
If you care about the fate of a promise, always return it.
In your case, findMore does not return the promise from request, so getData has no handle to track the resolution (or rejection) of that promise.
You can track the resolution of multiple promises with Promise.all.
The Promise.all() method returns a single Promise that resolves when all of the promises in the iterable argument have resolved or when the iterable argument contains no promises. It rejects with the reason of the first promise that rejects.
Lets put these to use on your example:
function getData() {
var options = {
method: 'GET',
uri: 'https://api.call1.com',
qs: {
access_token: _accessToken,
}
};
return request(options)
.then(function(apires){
var obj = JSON.parse(apires);
var findMorePromises = obj.data.map(function(entry) {
return findMore(entry.id)
});
return Promise.all(findMorePromises);
})
}
function findMore(id) {
var options = {
method: 'GET',
uri: 'https://api.call2.com',
qs: {
access_token: _accessToken,
}
};
return request(options)
.then(function(apires){
return JSON.parse(apires);
})
}
I've used map to construct the array of promises, but you could just as well use a foreach and push into an array similar to be more similar to your example code.
It's also good practice to make sure you are handling rejection of any promises (via catch), but I'll assume that is out of the scope of this question.

You want to use Promise.all.
So first thing first, you need an array of promises. Inside your for each loop, set findMore to a variable, and make it return the promise. Then have a line where you do Promise.all(promiseArr).then(function(){console.log("done)})
Your code would look like this
function getData() {
var promiseArr = []
var options = {
method: 'GET',
uri: 'https://api.call1.com',
qs: {
access_token: _accessToken,
}
};
request(options).then(function(apires){
console.log("complete 1");
var obj = JSON.parse(apires);
obj.data.forEach(function(entry) {
var p = findMore(entry.id)
promiseArr.push(p)
});
}).then(function(){
Promise.all(promiseArr).then(function(){
console.log("this is all done")
})
})
}
function findMore(id) {
var options = {
method: 'GET',
uri: 'https://api.call2.com',
qs: {
access_token: _accessToken,
}
};
return request(options).then(function(apires){
console.log("complete 2");
var obj = JSON.parse(apires);
})
}
the basic idea of Promise.all is that it only executes once all promises in the array have been resolved, or when any of the promises fail. You can read more about it here

You need to use Promise.all to run all async requests in parallel. Also you must return the result of findMore and getData (they are promises).
function getData() {
var options = {...};
return request(options)
.then(function(apires) {
console.log("complete 1");
var obj = JSON.parse(apires);
var ops = obj.data.map(function(entry) {
return findMore(entry.id);
});
return Promise.all(ops);
}
function findMore(id) {
var options = {...};
return request(options)
.then(function(apires) {
console.log("complete 2");
return JSON.parse(apires);
});
}
getData()
.then(data => console.log(data))
.catch(err => console.log(err));
If you can use ES7, it can be written with async/await:
let getData = async () => {
let options = {...};
let res = awit request(options);
let ops = res.data.map(entry => findMore(entry.id));
let data = await Promise.all(ops);
return data;
};
let findMore = async (id) => {
let options = {...};
let apires = awit request(options);
return JSON.parse(apires);
};

EDIT: As others have mentioned, using a Promise.all() is likely a better solution in this case.
If you are open to using jQuery (a JavaScript library), then you can use the .ajaxStop() event handler and specify your own function. Sample code:
$(document).ajaxStop(function(){
alert("All AJAX requests are completed.");
});
You will need to include the jQuery module. The instructions for Node.js are:
Install module through npm:
npm install jquery
Then use a "require" to use jQuery in your JavaScript code (a window with a document is required but there is no such "window" in Node so you can mock one with jsdom), see npm - jQuery for details:
require("jsdom").env("", function(err, window) {
if (err) {
console.error(err);
return;
}
var $ = require("jquery")(window);
});
If you want to stick to a pure JavaScript approach, you will need to create your own "module" to keep track of AJAX requests. In this module you can keep track of how many pending requests there are and remove them once they are terminated. Please see: Check when all Ajax Requests are complete - Pure JavaScript for more details.

Related

How do i pass result of callback into variable and access the var freely

I know there's a ton of question similar to my question, but i didn't see any good case to help me,
I have a callback from native function bridge and this how i used it on JS:
getAllParameter((data)=>{
console.log(data) // data is Javascript Object
})
I've tried this to get the value of data:
getAllParameter((data)=>{
return new Promise((resolve)=> resolve(showToken(data.Token)))
})
async function showToken(token){
var res = await token
return res
}
var isiToken = showToken()
console.log("isiToken")
console.log(isiToken)
but the result is:
isiToken
{ _40: 0, _65: 0, _55: null, _72: null }
i don't know whats wrong with my code, i want to get the value of data outside of the getAllParameter, how can i do that properly?
the reason why i need to get the result of getAllParameter and used it freely is because I've token value inside the data, and i need to use the token in axios instance config
so the full code of my file should be:
getAllParameter((data)=>{
return new Promise((resolve)=> resolve(showToken(data.Token)))
})
async function showToken(token){
var res = await token
console.log("res")
console.log(res)
return res
}
var isiToken = showToken()
console.log("isiToken")
console.log(isiToken)
const http = Axios.create ({
baseURL: Constants.APILink,
timeout: Constants.Timeout,
headers: {'Content-Type': 'application/json', 'Authorization': 'bearer '+isiToken}
export default http
});
I am not sure of your getAllParameter definition but that method should be calling your callback at the end. Hoping that it does that, here is snippet that does what you want
(function() {
var data;
function getAllParam(callback) {
console.log("getAllParam");
callback("getAllParam");
}
getAllParam((data)=> {
this.data = data);
console.log(this.data);
});
})();
So, what I am doing is
Creating a variable called data;
Assigning the callback response to my data variable. (Read closures & this in arrow functions)
Use that later.
But here is the limitation with my code: This doesn't work when getAllParam is an async function. Meaning if the callback is not called in sequence. You have to use promises then.
EDIT
app.js
function getAllParam(callback) {
console.log("getAllParam");
callback({Token: "getAllParam"});
}
var httpPromise = new Promise(resolve => {
getAllParam((data) => {
let token = data.Token;
console.log("Creating http from here using token");
let http = Axios.create({ bearer: token});
resolve(http);
})
});
export default httpPromise;
file_that_imports_app_js.js
import httpPromise from "./app.js";
async function init() {
let http = await httpPromise;
http.get("/", ...)
}
init();

async.queue within a promise chain?

I am trying to create an async queue for an array of get requests to an api, i am just unsure how to combine and use the responses. Maybe my implementation is wrong since i am using async.queue inside a promise then function ?
Ultimately i would like to get results from first promise ->
use results of that first promise to create an array of get requests for the async.queue ->
then combine the results of all the get responses. I need to throttle the amount of requests that go out at a time due to API rate limit.
const rp = require("request-promise");
app.get("/", (req,res) => {
let arr = []
rp.get(url)
.then((response) => {
let arrayID = response
let q = async.queue((task, callback) => {
request({
method: "GET",
url: url,
qs: {
id: task.id
}
}, (error, response, body) => {
arr.push(body)
console.log(arr.length)
// successfully gives me the response i want. im trying to push into an array with all of my responses,
// but when i go to next then chain it is gone or if i try to return arr i get an empty []
})
callback()
}, 3)
for(var i = 0; i < arrayID.length; i++){
q.push({ id : arrayID[i]} );
}
q.drain = function() {
console.log('all items have been processed');
}
return arr
})
.then((responseArray) => {
//empty array even though the length inside the queue said other wise, i know its a problem with async and sync actions but is there a way to make the promise chain and async queue play nice?
res.json(responseArray)
})
})
Figured it out, ended up having to wrap it in a promise and resolve the final array in q.drain()
const rp = require("request-promise");
app.get("/", (req,res) => {
rp.get(url)
.then((response) => {
let arrayID = response
return new Promise((resolve, reject) => {
var q = async.queue((task, callback) => {
request({
method: "GET",
url: url,
qs: {
id:task.id,
},
}, (error, response, body) => {
arr.push(body)
callback();
})
}, 2);
q.drain = () => resolve(arr);
q.push(arrayID);
})
})
.then((response) => res.json(response))
.catch((error) => res.json(error))
}
To launch multiple async calls in parallel you can use Promise.all()
To launch multiple async calls sequentially (i.e they depend on each other) you can return each promise and use its result inside a then() function
Code below:
app.get("/", (req,res)
.then(function(firstResult)) {
//You can use result of first promise here
return Promise.all([
//Create array of get request here
//To also return firstResult just add it in the Promise.All array
]);
})
.then(function(allResults){
//You can use results of all the get requests created in the previous then()
})
.catch(function(error){
//Deal with any error that happened
});

chaining promises to force async

I'm using promises to fetch large albums of images and when pull random samples from that album. I have managed to request all the albums and then push the links to images to an array of objects.
Now I want to print out that array but only after I've actually filled it. Whenever I add a .then() on the end it prints out only the initialized empty array.
What can I do to force async and only print the array once it's filled. (I'm printing it out at the bottom)
let findImagesCatalyst = new Promise(function(resolve, reject) {
//url options
const options = {
url: 'https://api.imgur.com/3/gallery/hot/time/',
headers: {
"Authorization": "Client-ID xxxx"
}
};
//inital request
request(options, function(err, res, body) {
//parse the response
body = JSON.parse(body)
//access the data in the response
const responseData = body.data;
//filter only those with image counts great than 50
const largeAlbums = responseData.filter(findDumps)
//test to see if a dump is present
if (largeAlbums.length > 0) {
largeAlbums.forEach(function(i) {})
resolve(largeAlbums)
} else {
reject()
}
})
})
//if successful in finding a dump, then go through them and find their albumIds
.then(function(largeAlbums) {
let dumpIds = largeAlbums.map(index => index.id)
return dumpIds;
})
//with the album/dump ids, get each of them with a new request
.then(function(dumpIds) {
//for each of the dumpIds create the needed url using ES6 and then request it.
dumpIds.forEach(function(i) {
const albumUrlOptions = {
url: `https://api.imgur.com/3/album/${i}/images`,
headers: {
"Authorization": "Client-ID xxxx"
}
}
//make a request to each of the albums/dumps
request(albumUrlOptions, function(err, res, body) {
body = JSON.parse(body)
const responseData = body.data
//pick one sample image from the album/dump
let sampleImage = responseData[randomSelector(responseData.length)].link;
dumps.push({
"dump": i,
'sample': sampleImage
})
})
})
return dumps;
})
.then(function(dumps) {
console.log(dumps)
})
You're second .then should return Promise.all of the (promisified) requests
.then(function(dumpIds) {
//for each of the dumpIds create the needed url using ES6 and then request it.
return Promise.all(dumpIds.map(function(i) {
const albumUrlOptions = {
url: `https://api.imgur.com/3/album/${i}/images`,
headers: {
"Authorization": "Client-ID xxxx"
}
};
return new Promise((resolve, reject) => {
//make a request to each of the albums/dumps
request(albumUrlOptions, function(err, res, body) {
body = JSON.parse(body)
const responseData = body.data
//pick one sample image from the album/dump
let sampleImage = responseData[randomSelector(responseData.length)].link;
resolve({
"dump": i,
'sample': sampleImage
});
});
});
}))
})
As you are using node.js, which has very good ES2015+ implementation, you can simplify (in my opinion) your code by, firstly, creating a "promisified version of request
let requestP = (options) => new Promise((resolve, reject) => {
request(options, (err, res, body) => {
if (err) {
return reject(err);
}
resolve({res, body});
});
});
The rest of the code could be then re-written as follows
const options = {
url: 'https://api.imgur.com/3/gallery/hot/time/',
headers: {
"Authorization": "Client-ID xxxx"
}
};
//inital request
let findImagesCatalyst = requestP(options)
.then(({res, body}) => {
//parse the response
body = JSON.parse(body)
//access the data in the response
const responseData = body.data;
//filter only those with image counts great than 50
const largeAlbums = responseData.filter(findDumps)
//test to see if a dump is present
if (largeAlbums.length > 0) {
largeAlbums.forEach(function(i) {})
return(largeAlbums)
} else {
return Promise.reject();
}
})
//if successful in finding a dump, then go through them and find their albumIds
.then((largeAlbums) => largeAlbums.map(index => index.id))
//with the album/dump ids, get each of them with a new request
.then((dumpIds) =>
//for each of the dumpIds create the needed url using ES6 and then request it.
Promise.all(dumpIds.map((i) => {
const albumUrlOptions = {
url: `https://api.imgur.com/3/album/${i}/images`,
headers: {
"Authorization": "Client-ID xxxx"
}
};
return requestP(albumUrlOptions)
.then(({res, body}) => {
body = JSON.parse(body)
const responseData = body.data
//pick one sample image from the album/dump
let sampleImage = responseData[randomSelector(responseData.length)].link;
return({
"dump": i,
'sample': sampleImage
});
});
}))
)
.then(function(dumps) {
console.log(dumps)
});
So, you have a few building blocks here:
Request for imgur albums reflected into options object.
findDumps — a simple function that you filter the list of albums against.
A function that applies the preceding two and returns an array of large albums. It's an asynchronous function, so it likely employs Promise.
A function that takes every item of the array of large albums and receives a single image. It's an asynchronous function, so, again, a Promise.
You want to wait until all the single images have been received.
Finally, you expect an array of objects of two properties: "dump" and "sample".
Let's try to contruct an example.
const findImagesCatalyst = new Promise((resolveImagesCatalyst, rejectImagesCatalyst) => {
const options = {
url: 'https://api.imgur.com/3/gallery/hot/time/',
headers: {
Authorization: 'Client-ID xxxx'
}
};
request(options, (err, res, body) => {
//access the data in the response
const responseData = JSON.parse(body).data;
//filter only those with image counts great than 50
const largeAlbums = responseData.filter(findDumps);
//test to see if a dump is present
if (largeAlbums.length > 0) {
// /!\ The trickiest part here: we won't resolve this promise until an "inner Promise" has been resolved.
// Note that next line declares a new function to resolve inner Promise, resolveLargeAlbum. Now we have two functions:
// - resolveImagesCatalyst - to resolve the main Promise, and
// - resolveLargeAlbum — to resolve every image request, and there can be many of them.
const imagesPromises = largeAlbums.map(largeAlbum => new Promise((resolveLargeAlbum, rejectLargeAlbun) => {
// take id from every large album
const dumpId = largeAlbum.id;
// declare options for inner request
const options = {
url: `https://api.imgur.com/3/album/${i}/images`,
headers: {
"Authorization": "Client-ID xxxx"
}
};
request(albumUrlOptions, (err, res, body) => {
const responseData = JSON.parse(body).data;
//pick one sample image from the album/dump
const sampleImage = responseData[randomSelector(responseData.length)].link;
if (sampleImage) {
// A-HA!
// It's inner Promise's resolve function. For N albums, there will be N resolveLargeAlbum calls. Just a few lines below, we're waiting for all of them to get resolved.
resolveLargeAlbum({
dump: dumpId,
sample: sampleImage
});
} else {
rejectLargeAlbun('Sorry, could not receive sample image:', dumpId, responseData);
}
});
}));
// Now we have imagePromises, an array of Promises. When you have an array of Promises, you can use Promise.all to wait until all of them are resolved:
Promise.all(imagesPromises).then(responses => {
// Take a look at responses: it has to be an array of objects of two properties: dump and sample.
// Also, note that we finally use outer Promise's resolve function, resolveImagesCatalyst.
resolveImagesCatalyst(responses);
}).catch(errors => {
rejectImagesCatalyst(errors);
});
} else {
rejectImagesCatalyst('Sorry, nope.');
}
});
});
That's a huge one. What you really need to see is that
With Promise.all, you can wait for a collection of Promises to get resolved, and the "then" part won't get executed until all of them have been resolved.
You can put a Promise into a Promise, and resolve outer Promise when inner Promise gets resolved.
The code is really hard to read, because the order of execution is not top-to-bottom. If you use Webpack with Babel, you might want to take a look at async/await. With async/await, the code looks synchronous: you read it from top to bottom and that's exactly the order results of its execution appear, but under the hood, it's all asynchronous. Pretty neat ES6 feature, imho.
Make sure there is no existing Node module that is handles your imgur searching business. Search on npms.io.
If there is no existing module, find one that is close and expand it for your use case (hot images).
If you really can't find a module for imgur to expand, then make your own. All of the imgur request stuff goes in its own module (and own file).
Make sure that module supports promises.
Your code should look something like this:
import {getHotAlbums, getAlbumImages, config} from 'imgur';
config({clientID: 'BLAHXXXX'});
async function getHotImages() {
let hotAlbums = await getHotAlbums();
hotAlbums = hotAlbums.filter(a => a.imageCount > 50);
const sampleImages = [];
let albumIDs = hotAlbums.map(a => a.id);
for (let albumID of albumIDs) {
const images = await getAlbumImages(albumID);
const randomImageNum = Math.round(Math.random()*images.length)+1;
sampleImages.push(images[randomImageNum].link);
}
return sampleImages;
}

Multiple promise requests chained together in Javascript

I am attempting to chain together multiple asynchronous requests in JS. Basically, I want to retrieve the artist info from the LastFM API, then using that info lookup their top tracks.
So far, I can successfully get the artists info, have it return, and it prints out the info in the next step. However, once I try to do my second request for the top tracks, the body is never printed and it immediately moves on to the next step.
I have tried many many different combinations of this code with different kinds of requests and such but I haven't gotten any luck. I just want to successfully do request 1 if successful then follow it up by others in a proper order.
var artistInfo = {
method: 'GET',
url: 'http://localhost:3000/users/db/artistInfo/' + artistName
};
var topTracks = { method: 'GET',
url: 'http://localhost:3000/users/db/topTracks/' + artistName
};
/* Dependencies */
var Promise = require('bluebird');
var reqP = Promise.promisifyAll(require('request-promise'));
reqP(artistInfo)
.then(function(info) {
console.log("got here 1");
return info;
})
.then(function(artist) {
console.log(artist);
reqP(topTracks)
.then(function(body) {
console.log(body);
console.log("got here 2");
return body;
});
return 'test';
})
.then(function(content) {
console.log(content);
return 'test2';
})
.catch(function(err) {
throw err;
});
To sequence these two requests and have the outer .then() wait for both, you need to return the internal promise (the one inside the .then() handler) in order to chain them. If you don't return them, then then nothing is chained to the parent promise and therefore the parent promise doesn't wait for the child promise. See the line where I added return to return reqP(topTracks):
var artistInfo = {
method: 'GET',
url: 'http://localhost:3000/users/db/artistInfo/' + artistName
};
var topTracks = { method: 'GET',
url: 'http://localhost:3000/users/db/topTracks/' + artistName
};
/* Dependencies */
var Promise = require('bluebird');
var reqP = Promise.promisifyAll(require('request-promise'));
reqP(artistInfo)
.then(function(info) {
console.log("got here 1");
return info;
})
.then(function(artist) {
console.log(artist);
// *** add return here ***
return reqP(topTracks)
.then(function(body) {
console.log(body);
console.log("got here 2");
return body;
});
})
.then(function(content) {
console.log(content);
return 'test2';
})
.catch(function(err) {
throw err;
});
FYI, it doesn't look like your two requests depend upon one another so you could also do them in parallel:
var artistInfo = {
method: 'GET',
url: 'http://localhost:3000/users/db/artistInfo/' + artistName
};
var topTracks = { method: 'GET',
url: 'http://localhost:3000/users/db/topTracks/' + artistName
};
/* Dependencies */
var Promise = require('bluebird');
var reqP = Promise.promisifyAll(require('request-promise'));
Promise.all([reqP(artistInfo), reqP(topTracks)]).then(function(results){
// results[0] is artist
// results[1] is tracks
}, function(err){
// error here
});
First, I don't know why you are promisifying a promise-based library. There are a couple of ways to do this, but I am going to give you what I think is the cleanest way, but it requires bluebird (which you are using in your example anyway, so shouldn't be a problem)
It doesn't look like your second request depends on your first, you can use Promise.props()
/* Dependencies */
var Promise = require('bluebird');
var request = require('request-promise');
var artistInfo = request({
method: 'GET',
url: 'http://localhost:3000/users/db/artistInfo/' + artistName
});
var topTracks = request({
method: 'GET',
url: 'http://localhost:3000/users/db/topTracks/' + artistName
});
Promise.props({
artistInfo: artistInfo,
topTracks: topTracks
}).then(function(result) {
console.log(JSON.stringify(result.artistInfo,null,2));
console.log(JSON.stringify(result.topTracks,null,2));
})
.catch(function(err) {
console.error(err);
});
So what is going on here? Promise.props allows you to pass an object whose properties are promises. These promises will then execute in parallel and the promise won't be resolved until both are resolved (or it will drop to catch if either fail).
As for chaining promises, you need to make sure to return a promise. This is what allows you to avoid the ugly nesting
/* Dependencies */
var Promise = require('bluebird');
var request = require('request-promise');
request({
method: 'GET',
url: 'http://localhost:3000/users/db/artistInfo/' + artistName
})
.then(function(artistInfo) {
console.log(artistInfo);
return request({
method: 'GET',
url: 'http://localhost:3000/users/db/topTracks/' + artistName
});
})
.then(function(topTracks) {
console.log(topTracks);
})
.catch(function(err) {
console.error(err);
});

Why does fetch return a weird hash of integers?

I'm using fetch API with React Native.
My response follows a normal format of {"message": "error here"} if the status is >= 400, which I will show in a native popup.
I'm trying to call response.json() after detecting a failure, but it keeps putting everything in a weird format...
{ _45: 0, _81: 0, _65: null, _54: null }
For whatever reason... the actual response I want is located in _65... I have no idea what these random keys are.
So currently I'm having to access it via _bodyText, but I assume that is wrong because it's a private underscore method.
What am I doing wrong?
var API = (function() {
var base = 'https://example.com/api/v1';
var defaults = {
credentials: 'same-origin',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
};
var alertFailure = function(response) {
if (response.status >= 200 && response.status < 400) {
return response;
} else {
var json = JSON.parse(response._bodyText || '{}');
var message = json.message || 'There was a problem. Close the app, and try again later.';
var error = new Error(message);
error.response = response;
throw error;
}
};
var callAPI = function(url, opts) {
opts.headers['X-Version'] = 'v' + Package.version;
return fetch(base + url, opts)
.then(alertFailure)
.then((response) => {
return response.json();
})
.catch((error) => {
Alert.alert(null, error.message);
});
};
return {
get: function(url, opts) {
var fullOpts = Object.assign({}, defaults, opts);
return callAPI(url, fullOpts);
},
post: function(url, data, opts) {
var fullOpts = Object.assign({}, defaults, {
method: 'POST',
body: JSON.stringify(data || {})
}, opts);
return callAPI(url, fullOpts);
}
};
})();
The answer is that .json() returns a promise... so I had to do everything from within .then()
AsyncStorage.getItems always returns a promise. You can use this method below
AsyncStorage.getItem("access_key").then((value)=>
{
console.log(value);
});
I would recommend you to use the new ES7 syntax async/await, they are easier to understand than using .then()
To use it, just declare some method with the async prefix and inside of it use await to wait for the call to finish.
E.g
async someMethod(){
result = await fetch(URL); // Do some request to any API.
// Use the result after that knowing that you'll have it completed.
}
I hope this helps, at least in my opinion, I find this easier than using .then(), especially when you have to do multiple calls within the same method.

Categories

Resources