Here's my example code:
function recursiveFetch(num) {
// EXAMPLE that recursivley fetches all todos from example API
return new Promise(resolve => {
fetch("https://jsonplaceholder.typicode.com/todos/" + num)
.then((response) => {
return response.json();
})
.then((data) => {
if (num == 0) {
console.log("Done getting TODOs");
resolve(num);
} else {
recursiveFetch(num - 1);
}
});
});
}
new Promise(resolve => {
// Just using this for this example
resolve(10);
})
.then((num) => {
// This runs fine and returns a promise which is eventually resolved
return recursiveFetch(num);
})
.then((num) => {
// This never happens?
console.log("num is now " + num);
})
I can't tell why but the second .then is never run.
If I execute this code in the Firefox console I get the output Done getting TODOs but the "num is now " log is never called?
To fix your code as it is, you need
recursiveFetch(num - 1).then(resolve)
However, there are quite a few mistakes, how about this cleaned-up one:
async function fetchAll(num) {
let data = [];
for (let i = 1; i <= num; i++) {
let t = await fetch("https://jsonplaceholder.typicode.com/todos/" + i);
data.push(await t.json())
}
return data;
}
Promise
.resolve(3)
.then(fetchAll)
.then(console.log)
You need to add a resolve inside the "else" when you call recursively the function "recursiveFecth". Since the function is returning another promise you need to resolve then, otherwise it will exit inmediately.
I've tried it and it works:
function recursiveFetch(num) {
// EXAMPLE that recursivley fetches all todos from example API
return new Promise(resolve => {
fetch("https://jsonplaceholder.typicode.com/todos/" + num)
.then((response) => {
return response.json();
})
.then((data) => {
if (num == 0) {
console.log("Done getting TODOs");
resolve(num);
} else {
resolve(recursiveFetch(num - 1));
}
});
});
}
new Promise(resolve => {
// Just using this for this example
resolve(10);
})
.then((num) => {
// This runs fine and returns a promise which is eventually resolved
return recursiveFetch(num);
})
.then((num) => {
// This never happens?
console.log("num is now " + num);
})
You need to return the recursiveFetch in the inner promise
...
.then((data) => {
if (num == 0) {
console.log("Done getting TODOs");
resolve(num);
} else {
return recursiveFetch(num - 1);
}
});
Related
In a promise, I want to assign a value to the property of several objects created from a class (in a loop), but when executing the function and doing the .then(() => console.log(r)) thing, the r does was not modified to what the promise promised me it would.
Here:
function assignSentenceImageDescription () {
return new Promise((resolve, reject) =>
{
assigningWordsPartOFSpeech().then((r) => {
JSON.parse(r).sentences.forEach((sentence) => {
let adjectiveBeforeNoun = [];
let sentenceImageDescription = [];
sentence.words.forEach((wordInSentence) => {
try {
if (wordInSentence.partOfSpeech[0].wordtype === "n.") {
let imageDescription = adjectiveBeforeNoun.join('') + wordInSentence.partOfSpeech[0].word;
sentenceImageDescription.push(imageDescription)
adjectiveBeforeNoun = [];
} else if (wordInSentence.partOfSpeech[0].wordtype === "superl.") {
adjectiveBeforeNoun.push(wordInSentence.partOfSpeech[0].word + " ")
}
} catch (e) {
console.log("===NOT IN DICTIONARY===")
}
})
sentence.imageDescription = sentenceImageDescription;
}
)
resolve(r);
}
);
}
);
}
On the line
sentence.imageDescription = sentenceImageDescription;
I try to assign the image description of each of the sentences iterated over, but when I do
assignSentenceImageDescription().then(r => console.log(r));
the r object still does not have each of its sentences's imageDescription property modified to the value the array sentenceImageDescription has, which is what the assignSentenceImageDescription() function is intended to do.
Refactor your code as follows:
Note: you don't need a Promise constructor since assigningWordsPartOFSpeech returns a Promise that you can work with (and return)
Set sentences = JSON.parse(r).sentences;
Now you can iterate through sentences as you already do, then simply return sentences in the .then - and you're done
function assignSentenceImageDescription() {
return assigningWordsPartOFSpeech().then((r) => {
const data = JSON.parse(r);
data.sentences.forEach((sentence) => {
let adjectiveBeforeNoun = [];
let sentenceImageDescription = [];
sentence.words.forEach((wordInSentence) => {
try {
if (wordInSentence.partOfSpeech[0].wordtype === "n.") {
let imageDescription = adjectiveBeforeNoun.join('') + wordInSentence.partOfSpeech[0].word;
sentenceImageDescription.push(imageDescription)
adjectiveBeforeNoun = [];
} else if (wordInSentence.partOfSpeech[0].wordtype === "superl.") {
adjectiveBeforeNoun.push(wordInSentence.partOfSpeech[0].word + " ")
}
} catch (e) {
console.log("===NOT IN DICTIONARY===")
}
})
sentence.imageDescription = sentenceImageDescription;
});
return data;
});
}
function page() {
return new Promise((resolve, reject) => {
setTimeout(function() {
fo = $("#root>div>div>main>div>div:nth-child(3)>div>div:nth-child(2)>div>div>div:nth-child(2)>div:nth-child(2)>div>div:nth-child(2)>div>div:nth-child(2)>div>ul>li:nth-last-child(1)")
fo.click()
console.log('翻页')
resolve();
}, 200)
})
}
function l() {
for (let i = 0, p = Promise.resolve(); i < 10; i++) {
p = p.then(() => {
return new Promise(resolve =>
setTimeout(function() {
console.log('wo')
$('#root>div>div>main>div>div:nth-child(3)>div>div:nth-child(2)>div>div>div:nth-child(2)>div:nth-child(2)>div>div:nth-child(2)>div>div:nth-child(1)>div:nth-child(1)>table>tbody>tr>td:nth-child(7)>div>div:nth-child(2)>a>span').eq(i).click()
resolve();
}, 200)
)
})
.then(() => {
return new Promise(resolve =>
setTimeout(function() {
console.log('wocao')
$('body>div:nth-last-child(1)>div>div>div:nth-child(3)>div:nth-child(2)>button>span').click()
resolve();
}, 200)
)
}
)
}
}
Promise.resolve().then(l).then(page)
Why is the program working not in order of the promises? How can I solve this problem? I've tried many times but they still execute together but not in order. Could somebody teach me? Very thanks.
Your function l is a unit function (i.e. returns nothing) and is therefore not awaitable as it does not return a Promise (either explicitly, or via being marked as async).
You should:
function l() {
let p = Promise.resolve() // this needs to exist out of the loop scope
for (let i = 0; i < 10; i++) {
p = p.then( /* ... */ )
}
return p; // <-- so it can be returned here
}
You need to resolve inside the timeout
new Promise(function(resolve, reject) {
// the function is executed automatically when the promise is constructed
// after 1 second signal that the job is done with the result "done"
setTimeout(() => {console.log("done");resolve()}, 1000);
}).then(function (res2, rej2) {
setTimeout(() => {console.log("done2")}, 1000);
})
but in your case you do not define parameters to the promise, hence they are default.
I have a Subject, which used to be fire async chain.
Its sometimes looks like working correctly, sometimes looks like the subscribe doesn't wait for the resolve.
behavior.subscribe(async (result)=> {
await fnc1(result);
});
fnc1(in) {
return new Promise(async (resolve,reject) => {
for(let w of in.split(',')) {
await fnc2(w);
}
//if for finished, go to next subscribed value
resolve();
});
}
fnc2(w) {
return new Promise((resolve,reject) => {
let i = 0;
setInterval( () => {
i = i + 1;
if(i == 10) {
//go next value in for loop
resolve();
}
},100);
});
}
For the following function, I have to add a timeout after every GET request in array ajaxUrls. All the XHR GET request are in array ajaxUrls.
function getAllSearchResultProfiles(searchAjaxUrl) {
var ajaxUrls = [];
for (var i = 0; i < numResults; i += resultsPerPage) {
ajaxUrls.push(searchAjaxUrl + "&start=" + i);
}
return Promise.all(ajaxUrls.map(getSearchResultsForOnePage))
.then(function(responses) {
return responses.map(function(response) {
if (response.meta.total === 0) {
return [];
}
return response.result.searchResults.map(function(searchResult) {
return (searchResult);
});
});
})
.then(function(searchProfiles) {
return [].concat.apply([], searchProfiles);
})
.catch(function(responses) {
console.error('error ', responses);
});
}
function getSearchResultsForOnePage(url) {
return fetch(url, {
credentials: 'include'
})
.then(function(response) {
return response.json();
});
}
I want a certain timeout or delay after every GET request. I am facing difficulty in where exactly to add the timeout.
If you want to make requests in serial, you shouldn't use Promise.all, which initializes everything in parallel - better to use a reduce that awaits the previous iteration's resolution and awaits a promise-timeout. For example:
async function getAllSearchResultProfiles(searchAjaxUrl) {
const ajaxUrls = [];
for (let i = 0; i < numResults; i += resultsPerPage) {
ajaxUrls.push(searchAjaxUrl + "&start=" + i);
}
const responses = await ajaxUrls.reduce(async (lastPromise, url) => {
const accum = await lastPromise;
await new Promise(resolve => setTimeout(resolve, 1000));
const response = await getSearchResultsForOnePage(url);
return [...accum, response];
}, Promise.resolve([]));
// do stuff with responses
const searchProfiles = responses.map(response => (
response.meta.total === 0
? []
: response.result.searchResults
));
return [].concat(...searchProfiles);
}
Note that only asynchronous operations should be passed from one .then to another; synchronous code should not be chained with .then, just use variables and write the code out as normal.
I find a simple for loop in an async function to be the most readable, even if not necessarily the most succinct for things like this. As long as the function is an async function you can also create a nice pause() function that makes the code very easy to understand when you come back later.
I've simplified a bit, but this should give you a good idea:
function pause(time) {
// handy pause function to await
return new Promise(resolve => setTimeout(resolve, time))
}
async function getAllSearchResultProfiles(searchAjaxUrl) {
var ajaxUrls = [];
for (var i = 0; i < 5; i++) {
ajaxUrls.push(searchAjaxUrl + "&start=" + i);
}
let responses = []
for (url of ajaxUrls) {
// just loop though and await
console.log("sending request")
let response = await getSearchResultsForOnePage(url)
console.log("recieved: ", response)
responses.push(response)
await pause(1000) // wait one second
}
//responses.map() and other manilpulations etc...
return responses
}
function getSearchResultsForOnePage(url) {
//fake fetch
return Promise.resolve(url)
}
getAllSearchResultProfiles("Test")
.then(console.log)
If you want to add a delay in every request then add a setTimout() in your function which fetches data from api
function getSearchResultsForOnePage(url) {
return new Promise((resolve, reject) => {
fetch(url, {
credentials: 'include'
})
.then(response => reresponse.json())
.then(data => {
let timeout = 1000;
setTimeout(() => resolve(data), timeout);
});
}
What I want to do is wait for the second promise to finish, concatenate the data i.e data = data.concat(items) and then increment the count making the loop run specified times. It's all written in AngularJS.
DataService.getSomeData().then(function(data) {
let count = 1;
while (count < 3) {
if (someCondition) { // It evaluates to true
// Second Promise
DataService.getUserData().then(function(items) {
data = data.concat(items); // --------> this should run before incrementing the count
});
count++;
}
$scope.myData = data;
}
});
Thanks!
Keep the api returning promises - its the easiest to handle and most predictable...
DataService.getSomeData()
.then(someData => {
let count = 1;
const promises = [];
while (count < 3) {
if (someCondition) { // It evaluates to true
promises.push(DataService.getUserData());
count++;
}
}
return $q.all(promises)
.then(data => data.reduce((memo, data) => memo.concat(data), someData));
})
.then(data => $scope.myData = data);
#Aleksey Solovey has already mentioned the solution which is to use $q.all(), there is another method of recursion which you can make use of.
DataService.getSomeData().then(function(data) {
getUserDataRecursion(data,0).then(result=>{
$scope.myData = result;
}).catch(error=>{
console.log("error handle ",error)
})
});
getUserDataRecursion(data,count){
return new Promise((resolve,reject)=>{
if(count<3){
if (someCondition){
DataService.getUserData().then((items) {
data = data.concat(items);
count++;
getUserDataRecursion(data,count),then(()=>{
resolve(data);
})
});
}else{
resolve(data);
}
}else{
resolve(data);
}
})
}