Say, I need to repeat an AJAX request N times sequentially.
Normally (i. e. without async/await), I would use reduce to chain promises:
function timeout (ms = 1000) {
return new Promise(resolve => setTimeout(resolve, ms))
}
function request (message, ms = 1000) {
return timeout(ms).then(() => console.log(message))
}
function makeArray (i) {
return new Array(i).fill(null);
}
makeArray(5).reduce(
(promise, _, i) => promise.then(() => request(i)),
Promise.resolve()
)
How do I rewrite this code with async/await?
Instead of promise.then(() => request(i)) I want to do await request(i).
All my attempts ended up running promises in parallel. I want sequentially.
Remember await can only be invoked from a an async function.
Here I've just wrapped inside a function run, it could even be an IIFE..
In most cases when you start using async / await, you'll be calling from another async function anyway, so this little run stub wouldn't be required.
function timeout (ms = 1000) {
return new Promise(resolve => setTimeout(resolve, ms))
}
function request (message, ms = 1000) {
return timeout(ms).then(() => console.log(message))
}
async function run() {
for (let trys = 0; trys < 5; trys ++) {
await request(trys);
}
}
run();
Related
Here's the code (it's a simple test, to see how this works so I can apply it to a larger work project):
async function wait(seconds) {
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}
async function printer(seconds) {
await wait(seconds);
console.log("Hello");
}
async function main() {
const list = [];
for (let i = 0; i < 10; i++) {
list.push(printer(i));
}
list.splice(0, 3);
await Promise.all(list);
}
main();
Desired behavior: Hello gets printed 7 times
Actual behavior: Hello gets printed 10 times
Rather than invoking each printer function when creating our list of promises, we can create a list of N functions, each with its own delay in seconds (from 1 to 10).
Once we've created our list of 10 functions, we can splice to drop the required amount, then call each function in turn to create our list of 7 promises.
Finally we can await the results by calling Promise.all().
async function wait(seconds) {
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}
function createPrinter(seconds) {
return () => printer(seconds);
}
async function printer(seconds) {
await wait(seconds)
console.log("Hello");
}
async function main() {
const list = [];
for (let i = 0; i < 10; i++) {
list.push(createPrinter(i));
}
list.splice(0, 3);
const promises = list.map(fn => fn());
await Promise.all(promises);
}
main();
.as-console-wrapper { max-height: 100% !important; }
As #VLAZ says in the comments, you are expecting a Promise to be something you execute, rather than what it is, just a notification mechanism that tells you if a piece of code has executed (and the code executes whether you check on the Promise or not).
When you call printer(i) the wait() function is called before your splice() method call can do anything to reduce the number of printer functions. So all of them execute, giving you 10 results.
If you want to postpone starting the waiting code until a time of your choosing, you need to rewrite in a way that allows to you to trigger the waiting. Here is a way which turns the printer functions into objects which you can then trigger after you have reduced the size of the array:
function wait(seconds) {
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}
class Printer {
constructor(seconds) {
this.seconds = seconds
}
async printAfterDelay() {
await wait(this.seconds);
console.log("Hello", this.seconds);
}
}
async function main() {
const list = [];
for (let i = 0; i < 10; i++) {
list.push(new Printer(i));
}
list.splice(0, 3);
const printingPromises = list.map(
printd => printd.printAfterDelay() // starts the waiting
)
await Promise.all(printingPromises); // This code doesn't really do anything as there is no more code to execute
}
main();
Here i am trying to wrap my head around promises.Here on first request i fetch a set of links.and on next request i fetch the content of first link.But i want to make a delay before returning next promise object.So i use setTimeout on it. But it gives me the following JSON error (without setTimeout() it works just fine)
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of
the JSON data
i would like to know why it fails?
let globalObj={};
function getLinks(url){
return new Promise(function(resolve,reject){
let http = new XMLHttpRequest();
http.onreadystatechange = function(){
if(http.readyState == 4){
if(http.status == 200){
resolve(http.response);
}else{
reject(new Error());
}
}
}
http.open("GET",url,true);
http.send();
});
}
getLinks('links.txt').then(function(links){
let all_links = (JSON.parse(links));
globalObj=all_links;
return getLinks(globalObj["one"]+".txt");
}).then(function(topic){
writeToBody(topic);
setTimeout(function(){
return getLinks(globalObj["two"]+".txt"); // without setTimeout it works fine
},1000);
});
To keep the promise chain going, you can't use setTimeout() the way you did because you aren't returning a promise from the .then() handler - you're returning it from the setTimeout() callback which does you no good.
Instead, you can make a simple little delay function like this:
function delay(t, v) {
return new Promise(resolve => setTimeout(resolve, t, v));
}
And, then use it like this:
getLinks('links.txt').then(function(links){
let all_links = (JSON.parse(links));
globalObj=all_links;
return getLinks(globalObj["one"]+".txt");
}).then(function(topic){
writeToBody(topic);
// return a promise here that will be chained to prior promise
return delay(1000).then(function() {
return getLinks(globalObj["two"]+".txt");
});
});
Here you're returning a promise from the .then() handler and thus it is chained appropriately.
You can also add a delay method to the Promise object and then directly use a .delay(x) method on your promises like this:
function delay(t, v) {
return new Promise(resolve => setTimeout(resolve, t, v));
}
Promise.prototype.delay = function(t) {
return this.then(function(v) {
return delay(t, v);
});
}
Promise.resolve("hello").delay(500).then(function(v) {
console.log(v);
});
.then(() => new Promise((resolve) => setTimeout(resolve, 15000)))
UPDATE:
when I need sleep in async function I throw in
await new Promise(resolve => setTimeout(resolve, 1000))
The shorter ES6 version of the answer:
const delay = t => new Promise(resolve => setTimeout(resolve, t));
And then you can do:
delay(3000).then(() => console.log('Hello'));
If you are inside a .then() block and you want to execute a settimeout()
.then(() => {
console.log('wait for 10 seconds . . . . ');
return new Promise(function(resolve, reject) {
setTimeout(() => {
console.log('10 seconds Timer expired!!!');
resolve();
}, 10000)
});
})
.then(() => {
console.log('promise resolved!!!');
})
output will as shown below
wait for 10 seconds . . . .
10 seconds Timer expired!!!
promise resolved!!!
Happy Coding!
Since node v15, you can use timers promise API
example from the doc:
import { setTimeout } from 'timers/promises'
const res = await setTimeout(100, 'result')
console.log(res) // Prints 'result'
In node.js you can also do the following:
const { promisify } = require('util')
const delay = promisify(setTimeout)
delay(1000).then(() => console.log('hello'))
For the current LTS its easier and we can use async/await to handle timeouts. Please note that this is the recommended way to use timeout nowadays.
Thenables is not the recommended way.
const { promisify } = require('util')
const sleep = promisify(setTimeout)
async function myFunction() {
await sleep(1e3)
console.log('This will be seen after 1 sec')
await sleep(5e3)
console.log('This will be seen after 5 sec after')
}
const myStuff = new Promise(function (resolve) {
console.log("before timeout");
setTimeout(
function (x) {
console.log("inside the timeout");
resolve(x);
},
3000,
"after timeout"
);
}).then((response) => console.log(response));
I have to run an async function written with Puppeteer (it's a bot) and I want to run that function every 60 seconds. The issue is that the async function (bot) runs for 2 min and so node cron automatically executes another async function which leads to 2 bot and 5s later it will launch another instance of this function.
What i want is to run 1st time then wait for 60s And after that execute it again. I'm so confused, it's my first NodeJS project.
const cron = require("node-cron")
cron.schedule("*/60 * * * * *", async () => {
try {
console.log(BotVote("Royjon94", "1"))
}
catch {
console.log('error 404')
}
})
I'm not sure cron is suitable for this kind of job. But basically you can achieve the same behavior with a basic while loop and a promise to await.
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function BotVote() {
await new Promise(r => setTimeout(() => {
console.log("voting");
r();
})) // for illusturation purpose
}
async function worker() {
let i = 0;
let keepGoing = true;
while (keepGoing) {
await BotVote()
await delay(600); // imagine it's 1minute.
i++;
if (i === 3) keepGoing = false; // decide when you stop
}
}
worker();
I would like to execute half of the a function and wait 1 second or 2 and then execute the rest, I tried like this, but I don't understand how to put a function inside another.
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
$('#music_action').click(function() {
if($(menu_start_animation).hasClass('menu_start_animation')) {
$(menu_start_animation).removeClass('menu_start_animation');
$(menu_start_animation).addClass('favorites_back_animation');
await sleep(2000);
$(menu_start_animation).removeClass('favorites_back_animation');
$(menu_start_animation).addClass('music_animation');
}
});
You just need to make your click callback async.
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
$('#music_action').click(async function () {
if ($(menu_start_animation).hasClass('menu_start_animation')) {
$(menu_start_animation).removeClass('menu_start_animation');
$(menu_start_animation).addClass('favorites_back_animation');
await sleep(2000);
$(menu_start_animation).removeClass('favorites_back_animation');
$(menu_start_animation).addClass('music_animation');
}
});
If you want to accomplish this using await, you should to make the function asynchronous or it will throw a syntax error, await wont work using regular functions. Here is an example of how to accomplish what you're trying to do asynchronously.
sleep = async (ms) => {
await new Promise((resolve, reject) => setTimeout(resolve, ms));
}
onClick = async () => {
console.log('first task')
// wait 2 seconds
await sleep(2000);
console.log('second task')
}
onClick()
However, for this use case you might not need to accomplish this asynchronously. Using setTimeout() seems async, but it runs concurrently in the background and uses less memory - asynchronous functions break a synchronous flow, but they don't necessarily execute in a concurrent order. In your case, it might be best to use a callback function.
/**
* Basic example updating a string
* In your case, the callback function would be adding and removing whatever you
* needed to in the second half of your function
*/
let word = 'init'
function sleep (callback, ms) {
setTimeout( () => {
word = 'updatedWord'
callback()
}, ms)
}
sleep(function () {
alert(word)
}, 2000)
I am trying to implement a sleep function using Promises in JavaScript.
function sleep(ms) {
var begin = new Promise(resolve => {
console.log("Sleep function called for " + ms + " ms\n")
});
return new Promise(resolve => setTimeout(resolve, ms))
.then(() => console.log("Sleep done!" ));
}
and it works. But,
function sleep(ms) {
var begin = new Promise(resolve => {
console.log("Sleep function called for " + ms + " ms\n")
});
return begin.then(resolve => setTimeout(resolve, ms))
.then(() => console.log("Sleep done!" ));
}
doesn't, rather it just hangs! What gives?
UPDATE: What I really want to do is write it out as a sequence of promise calls.
function sleep(ms) {
var beginAnnounce = new Promise(...);
var goSleep = new Promise (...);
var endAnnounce = new Promise...);
return beginAnnounce.then(goSleep).then(endAnnounce());
}
If you want to compose two promises, you can return the second one in a callback passed to Promise.prototype.then of the first promise.
Have a look at the following code:
const sleep = ms => () => new Promise((resolve, reject) => window.setTimeout(resolve, ms));
Promise.resolve()
.then(() => { console.log('A1');})
.then(sleep(2000))
.then(() => {console.log('A2');});
Promise.resolve()
.then(() => {console.log('B1');})
.then(sleep(1000))
.then(() => {console.log('B2');});
The sleep function is a higher order function which returns another function that returns a promise. This promise is resolved in the call to Window.setTimeout parametrized by the ms passed to sleep.
As you can see the executions are interleaved and you will see the log output for the statement console.log('B2') of the second promise before the output for console.log('A2'); of the first one.
In the second snippet, you need to resolve begin immediately
var begin = new Promise(resolve => {
console.log("Sleep function called for " + ms + " ms\n")
resolve()
})
The reason it works in the first snippet is because you never rely on begin to finish, you just have it there to log the start. But that's not the way you want to do it. There's no point having a Promise that resolves immediately (not for your use case anyway). So you should rather do something like:
function sleep(ms) {
console.log("Sleep function called for " + ms + " ms\n")
return new Promise(resolve => {
setTimeout(() => {
console.log("Sleep done!")
resolve()
}, ms)
})
}