Using Settimeout on Promise Chain

Using setTimeout on promise chain

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);
});

setTimeout with Promises

You're actually almost there. All you need is to assign the value of the resolved promise to result, instead of assigning the Promise object directly. This is done by using result = await double(<param1>, <param2>).

However, since JS does not yet support top-level await, you need to wrap your whole result assignment logic in another async function, and then call it as such:

function promisify(number, increase){    return new Promise(resolve => setTimeout(() => resolve(number * 2 + increase), 100))}
async function double(number, increase) { const value = await promisify(number, increase); return value;}
async function run() { let result; result = await double(5, 0) result = await double(10, result) result = await double(20, result)
console.log(result)}
run();

How to set a timeout inside a promise chain's .then()

Create a new promise, and resolve it inside of setTimeout callback:

Promise
.resolve("Hello")
.then((result) => {
console.log(result);
console.log("========== Then Block 1");
return "how are you";
})
.then(result => {
console.log(result);
console.log("========== Then Block 2");
return "I'm Fine! Thank you";
})
.then(result => new Promise(resolve => { // <== create a promise here
setTimeout(function() {
console.log("Time out Done!");
console.log(result);
console.log("========== Then Block 3");
resolve(); // <== resolve it in callback
}, 3000);
}))
.then(() => {
console.log("========== Then Block 4");
});

How to make a promise from setTimeout

Update (2017)

Here in 2017, Promises are built into JavaScript, they were added by the ES2015 spec (polyfills are available for outdated environments like IE8-IE11). The syntax they went with uses a callback you pass into the Promise constructor (the Promise executor) which receives the functions for resolving/rejecting the promise as arguments.

First, since async now has a meaning in JavaScript (even though it's only a keyword in certain contexts), I'm going to use later as the name of the function to avoid confusion.

Basic Delay

Using native promises (or a faithful polyfill) it would look like this:

function later(delay) {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
}

Note that that assumes a version of setTimeout that's compliant with the definition for browsers where setTimeout doesn't pass any arguments to the callback unless you give them after the interval (this may not be true in non-browser environments, and didn't used to be true on Firefox, but is now; it's true on Chrome and even back on IE8).

Basic Delay with Value

If you want your function to optionally pass a resolution value, on any vaguely-modern browser that allows you to give extra arguments to setTimeout after the delay and then passes those to the callback when called, you can do this (current Firefox and Chrome; IE11+, presumably Edge; not IE8 or IE9, no idea about IE10):

function later(delay, value) {
return new Promise(function(resolve) {
setTimeout(resolve, delay, value); // Note the order, `delay` before `value`
/* Or for outdated browsers that don't support doing that:
setTimeout(function() {
resolve(value);
}, delay);
Or alternately:
setTimeout(resolve.bind(null, value), delay);
*/
});
}

If you're using ES2015+ arrow functions, that can be more concise:

function later(delay, value) {
return new Promise(resolve => setTimeout(resolve, delay, value));
}

or even

const later = (delay, value) =>
new Promise(resolve => setTimeout(resolve, delay, value));

Cancellable Delay with Value

If you want to make it possible to cancel the timeout, you can't just return a promise from later, because promises can't be cancelled.

But we can easily return an object with a cancel method and an accessor for the promise, and reject the promise on cancel:

const later = (delay, value) => {
let timer = 0;
let reject = null;
const promise = new Promise((resolve, _reject) => {
reject = _reject;
timer = setTimeout(resolve, delay, value);
});
return {
get promise() { return promise; },
cancel() {
if (timer) {
clearTimeout(timer);
timer = 0;
reject();
reject = null;
}
}
};
};

Live Example:

const later = (delay, value) => {    let timer = 0;    let reject = null;    const promise = new Promise((resolve, _reject) => {        reject = _reject;        timer = setTimeout(resolve, delay, value);    });    return {        get promise() { return promise; },        cancel() {            if (timer) {                clearTimeout(timer);                timer = 0;                reject();                reject = null;            }        }    };};
const l1 = later(100, "l1");l1.promise .then(msg => { console.log(msg); }) .catch(() => { console.log("l1 cancelled"); });
const l2 = later(200, "l2");l2.promise .then(msg => { console.log(msg); }) .catch(() => { console.log("l2 cancelled"); });setTimeout(() => { l2.cancel();}, 150);

setTimeout function with Promise resolve value as a callback

Overall, your explanation looks good enough. Just steps 4 & 5 have a bit wrong.

Step: 4 ....Since our 'resolve' is empty we can't use '.then' method's argument
RIGHT...

The answer is NO.
You still can .then, and the data will be undefined

Sample Image

var wait = function (seconds) {
return new Promise(function (resolve) {
setTimeout(resolve, seconds * 1000);
});
};

wait(1)
.then(() => {
console.log(`1 second passed`);
}).then((data) => { console.log("Result: " + data) });

How do you wrap setTimeout in a promise

TL;DR - you've wrapped setTimeout in a promise properly, the issue is you are using it improperly

.then(promiseTimeout(2000)).then

will not do what you expect. The "signature" for .then is then(functionResolved, functionRejected)

A promise’s then method accepts two arguments:

promise.then(onFulfilled, onRejected)

Both onFulfilled and onRejected are optional arguments:

  • If onFulfilled is not a function, it must be ignored.
  • If onRejected is not a function, it must be ignored.

source: https://promisesaplus.com/#point-21

You are not passing a function to then

Consider the way you are doing it:

Promise.resolve('hello')
.then(promiseTimeout(2000))
.then(console.log.bind(console))

vs how it should be done:

Promise.resolve('hello').then(function() { 
return promiseTimeout(2000)
}).then(console.log.bind(console))

The first outputs 'hello' immediately

The second outputs 2000 after 2 seconds

Therefore, you should be doing:

it('should restore state when browser back button is used', function(done) {
r.domOK().then(function() {
xh.fire('akc-route-change', '/user/4/profile/new');
}).then(function() {
return promiseTimeout(2000);
}).then(function(t) {
xu.fire('akc-route-change', '/user/6');
}).then(function() {
return promiseTimeout(10);
}).then(function(t) {
expect(xu.params[0]).to.equal(6);
history.back();
}).then(function() {
return promiseTimeout(10);
}).then(function() {
expect(xu.params[0]).to.equal(4);
done();
});
});

Alternatively:

it('should restore state when browser back button is used', function(done) {
r.domOK().then(function() {
xh.fire('akc-route-change', '/user/4/profile/new');
}).then(promiseTimeout.bind(null, 2000)
).then(function(t) {
xu.fire('akc-route-change', '/user/6');
}).then(promiseTimeout.bind(null, 10)
).then(function(t) {
expect(xu.params[0]).to.equal(6);
history.back();
}).then(promiseTimeout.bind(null, 10)
).then(function() {
expect(xu.params[0]).to.equal(4);
done();
});
});

EDIT: March 2019

Over the years, things have changed a lot - arrow notation makes this even easier

Firstly, I would define promiseTimeout differently

const promiseTimeout = time => () => new Promise(resolve => setTimeout(resolve, time, time));

The above returns a function that can be called to create a "promise delay" and resolves to the time (length of delay). Thinking about this, I can't see why that would be very useful, rather I'd:

const promiseTimeout = time => result => new Promise(resolve => setTimeout(resolve, time, result));

The above would resolve to the result of the previous promise (far more useful)

But it's a function that returns a function, so the rest of the ORIGINAL code could be left unchanged. The thing about the original code, however, is that no values are needed to be passed down the .then chain, so, even simpler

const promiseTimeout = time => () => new Promise(resolve => setTimeout(resolve, time));

and the original code in the question's it block can now be used unchanged

it('should restore state when browser back button is used',function(done){
r.domOK().then(function(){
xh.fire('akc-route-change','/user/4/profile/new');
}).then(promiseTimeout(2000)).then(function(){
xu.fire('akc-route-change','/user/6');
}).then(promiseTimeout(10)).then(function(){
expect(xu.params[0]).to.equal(6);
history.back();
}).then(promiseTimeout(10)).then(function(){
expect(xu.params[0]).to.equal(4);
done();
});
});

Promise inside for loop with setTimeout

Your code basically runs 10000000 api requests simultaneously after 1 second delay. You need to chain API calls so they are run after another:

    const axios = require('axios');

function getData(i){
return axios.get(`http://www.api.com/${i}`);
}

function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

function dataLoop() {
const getOne = (i: number) => {
if (i < 10000000) {
getData(i)
.then((res) => {
console.log(`successs ${i}`);
})
.catch((err) => {
console.log(`fail ${i}`);
})
.then(wait(1000))
.then(() => getOne(i + 1))
}
}
getOne(0);
}
dataLoop();

I suggest to try using async/await if possible in your situation, it greatly simplifies things.

for (let i = 0; i < 10000000; i++) {
try {
const res = await getData(i);
console.log(`successs ${i}`);
} catch (e) {
console.log(`fail ${i}`);
}
await wait(1000);
}


Related Topics



Leave a reply



Submit