There are several conceptual things in the way of your approach doing what you want.
First, .map()
is synchronous. That means it runs to completion and doesn't wait for any async operations to finish.
Second, setTimeout()
is non-blocking. It just schedules a timer for some time in the future and then your .map()
callback returns immediately, returning nothing.
So, your approach doesn't work at all.
From your comments, it appears that what you're trying to accomplish is to make a bunch of network calls in a loop, but put a delay between them so you don't get rate limited. There are a bunch of ways to do that.
There are two basic concepts you need to make that work:
Make your async operations be sequential so the next one doesn't get initiated until the prior one is done.
Put a delay that works with promises before starting the next one.
I'll first show an ES7 approach using async/await
as it probably looks conceptually the simplest.
Using async/await
to sequence asynchronous array access
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
getNumbers().then(async function(numbersArray) {
//Supposed to return array of posts title
let results = [];
let delayT = 0; // first delay is zero
for (let number of numbersArray) {
console.log("Reading number" + number);
let r = await delay(delayT).then(() => {
delayT = 10 * 1000; // 10 seconds for subsequent delays
return getHtml("https://jsonplaceholder.typicode.com/posts/"+number).then(function(post) {
return post.title;
});
});
results.push(r);
}
return results;
});
Using .reduce()
to sequence asynchronous array acess
If you wanted to do it without async/await
, then you could use the .reduce()
design pattern for sequencing async iteration of an array:
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
getNumbers().then(numbersArray => {
//Supposed to return array of posts title
let results = [];
let delayT = 0; // first delay is zero
return numersArray.reduce((p, number) => {
return p.then(() => {
return delay(delayT).then(() => {
delayT = 10 * 1000; // 10 seconds for subsequent delays
return getHtml("https://jsonplaceholder.typicode.com/posts/"+number).then(function(post) {
results.push(post.title);
});
});
});
}, Promise.resolve()).then(() => {
// make array of results be the resolved value of the returned promise
return results;
});
});
Note that both of these algorithms are coded to not delay the first operation since presumably you don't need to, so it only delays between successive operations.
As coded, these are modeled after Promise.all()
and they will reject if any of your getHtml()
calls reject. If you want to return all results, even if some reject, then you can change:
return getHtml(...).then(...)
to
return getHtml(...).then(...).catch(err => null);
which will put null
in the returned array for any result that failed, or if you want to log the error, you would use:
return getHtml(...).then(...).catch(err => {
console.log(err);
return null;
});
Generic Helper Function
And, since this is a somewhat generic problem, here's a generic helper function that lets you iterate an array calling an async operation on each item in the array and accumulating all the results into an array:
// Iterate through an array in sequence with optional delay between each async operation
// Returns a promise, resolved value is array of results
async iterateArrayAsync(array, fn, opts = {}) {
const options = Object.assign({
continueOnError: true,
delayBetweenAsyncOperations: 0,
errPlaceHolder: null
}, opts);
const results = [];
let delayT = 0; // no delay on first iteration
for (let item of array) {
results.push(await delay(delayT).then(() => {
return fn(item);
}).catch(err => {
console.log(err);
if (options.continueOnError) {
// keep going on errors, let options.errPlaceHolder be result for an error
return options.errPlaceHolder;
} else {
// abort processing on first error, will reject the promise
throw err;
}
}));
delayT = options.delayBetweenAsyncOperations; // set delay between requests
}
return results;
}
This accepts options that let you continueOnError, lets you set the delay between each async operation and lets you control the placeholder in the array of results for any failed operation (only used if continueOnError
is set). All the options are optional.