Refactor nested promises to a chain of promises - javascript

I have simplified this problem. I have 3 functions which I want to run with a 2 second delay between each. The following code is working:
$.when(one()).done(function () {
$.when(delay(2000)).done(function () {
$.when(two()).done(function () {
$.when(delay(2000)).done(function () {
$.when(three()).done(function () {
console.log('finished');
});
});
});
});
});
function delay(ms) {
var waitForDelay = new $.Deferred();
setTimeout(function () {
waitForDelay.resolve().promise();
}, ms);
return waitForDelay.promise();
}
function one() {
console.log('one');
return new $.Deferred().resolve().promise();
}
function two() {
console.log('two');
return new $.Deferred().resolve().promise();
}
function three() {
console.log('three');
return new $.Deferred().resolve().promise();
}
However when I try to refactor it, it no longer waits for the delayed amounts of time:
one().done(delay(2000)).done(two).done(delay(2000)).done(three).done(function () {
console.log('finished');
});
How can I refactor to chain these promises rather than nesting them? I want to use jQuery for older browser compatibility.

My advice would be to not use jQuery's weird promise-like data structures at all.
Have your functions return an actual Promise (either explicitly or by making them async functions) and await each step in the chain.
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
// an async function implicitly returns a promise
const one = async () => {
console.log("one");
}
// or you can return one manually
const two = () => {
console.log("two");
return Promise.resolve();
}
const three = async () => {
console.log("three");
}
// await each step in an async function (an async IIFE in this case)
(async () => {
await one();
await delay(2000);
await two();
await delay(2000);
await three();
console.log("finished");
})();
You can also use .then() if you prefer that style
// Convenience function so you don't repeat yourself
const waitTwoSeconds = () => delay(2000);
one()
.then(waitTwoSeconds)
.then(two)
.then(waitTwoSeconds)
.then(three)
.then(() => {
console.log("finished");
});
If you must use jQuery, it doesn't really look much different
function delay(ms) {
var d = $.Deferred();
setTimeout(d.resolve, ms);
return d.promise();
}
function one() {
console.log("one");
return $.Deferred().resolve().promise();
}
function two() {
console.log("two");
return $.Deferred().resolve().promise();
}
function three() {
console.log("three");
return $.Deferred().resolve().promise();
}
function waitTwoSeconds() {
return delay(2000);
}
one()
.then(waitTwoSeconds)
.then(two)
.then(waitTwoSeconds)
.then(three)
.then(function() {
console.log("finished");
});
<!-- Note jQuery 1.8 was the first to have chainable deferreds -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
I've used .then() instead of .done() because the latter does not chain and using $.when() is unnecessary when .then() is available.

Taking inspiration from Phil's answer, here is a solution compatible with older browsers.
First, change the delay() function to return a function:
function delay(ms) {
return function() {
var waitForDelay = new $.Deferred();
setTimeout(function () {
waitForDelay.resolve().promise();
}, ms);
return waitForDelay.promise();
}
}
All other functions stay the same as in the question.
Finally replace the .done()s with .then()s to make them chain:
one().then(delay(2000)).then(two).then(delay(2000)).then(three).then(function () {
console.log('finished');
});

Related

How do I mock a delayed async function using setTimeout in Javascript [duplicate]

This is not a realworld problem, I'm just trying to understand how promises are created.
I need to understand how to make a promise for a function that returns nothing, like setTimeout.
Suppose I have:
function async(callback){
setTimeout(function(){
callback();
}, 5000);
}
async(function(){
console.log('async called back');
});
How do I create a promise that async can return after the setTimeout is ready to callback()?
I supposed wrapping it would take me somewhere:
function setTimeoutReturnPromise(){
function promise(){}
promise.prototype.then = function() {
console.log('timed out');
};
setTimeout(function(){
return ???
},2000);
return promise;
}
But I can't think beyond this.
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);
Original Answer from 2014
Usually you'll have a promise library (one you write yourself, or one of the several out there). That library will usually have an object that you can create and later "resolve," and that object will have a "promise" you can get from it.
Then later would tend to look something like this:
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise(); // Note we're not returning `p` directly
}
In a comment on the question, I asked:
Are you trying to create your own promise library?
and you said
I wasn't but I guess now that's actually what I was trying to understand. That how a library would do it
To aid that understanding, here's a very very basic example, which isn't remotely Promises-A compliant: Live Copy
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Very basic promises</title>
</head>
<body>
<script>
(function() {
// ==== Very basic promise implementation, not remotely Promises-A compliant, just a very basic example
var PromiseThingy = (function() {
// Internal - trigger a callback
function triggerCallback(callback, promise) {
try {
callback(promise.resolvedValue);
}
catch (e) {
}
}
// The internal promise constructor, we don't share this
function Promise() {
this.callbacks = [];
}
// Register a 'then' callback
Promise.prototype.then = function(callback) {
var thispromise = this;
if (!this.resolved) {
// Not resolved yet, remember the callback
this.callbacks.push(callback);
}
else {
// Resolved; trigger callback right away, but always async
setTimeout(function() {
triggerCallback(callback, thispromise);
}, 0);
}
return this;
};
// Our public constructor for PromiseThingys
function PromiseThingy() {
this.p = new Promise();
}
// Resolve our underlying promise
PromiseThingy.prototype.resolve = function(value) {
var n;
if (!this.p.resolved) {
this.p.resolved = true;
this.p.resolvedValue = value;
for (n = 0; n < this.p.callbacks.length; ++n) {
triggerCallback(this.p.callbacks[n], this.p);
}
}
};
// Get our underlying promise
PromiseThingy.prototype.promise = function() {
return this.p;
};
// Export public
return PromiseThingy;
})();
// ==== Using it
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise(); // Note we're not returning `p` directly
}
display("Start " + Date.now());
later().then(function() {
display("Done1 " + Date.now());
}).then(function() {
display("Done2 " + Date.now());
});
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
})();
</script>
</body>
</html>
const setTimeoutAsync = (cb, delay) =>
new Promise((resolve) => {
setTimeout(() => {
resolve(cb());
}, delay);
});
We can pass custom 'cb fxn' like this one 👆🏽
One-liner that wraps a promise around setTimeout
await new Promise(r => setTimeout(r, ms))
Example:
async someFunction() {
// Do something
// Wait 2 seconds
await new Promise(r => setTimeout(r, 2000))
// Do something else
}
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'
It uses signals much like browser fetch to handle abort, check the doc for more :)
Implementation:
// Promisify setTimeout
const pause = (ms, cb, ...args) =>
new Promise((resolve, reject) => {
setTimeout(async () => {
try {
resolve(await cb?.(...args))
} catch (error) {
reject(error)
}
}, ms)
})
Tests:
// Test 1
pause(1000).then(() => console.log('called'))
// Test 2
pause(1000, (a, b, c) => [a, b, c], 1, 2, 3).then(value => console.log(value))
// Test 3
pause(1000, () => {
throw Error('foo')
}).catch(error => console.error(error))
This is not an answer to the original question. But, as an original question is not a real-world problem it should not be a problem. I tried to explain to a friend what are promises in JavaScript and the difference between promise and callback.
Code below serves as an explanation:
//very basic callback example using setTimeout
//function a is asynchronous function
//function b used as a callback
function a (callback){
setTimeout (function(){
console.log ('using callback:');
let mockResponseData = '{"data": "something for callback"}';
if (callback){
callback (mockResponseData);
}
}, 2000);
}
function b (dataJson) {
let dataObject = JSON.parse (dataJson);
console.log (dataObject.data);
}
a (b);
//rewriting above code using Promise
//function c is asynchronous function
function c () {
return new Promise(function (resolve, reject) {
setTimeout (function(){
console.log ('using promise:');
let mockResponseData = '{"data": "something for promise"}';
resolve(mockResponseData);
}, 2000);
});
}
c().then (b);
JsFiddle
The simplest way
(async function() {
console.log('1');
await SleepJs(3000);
console.log('2');
} )();
function SleepJs(delay) {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
}
If none of those solutions worked for you, please try this
const asyncTimeout = (ms) => {
// when you make a promise you have to resolve it or reject it
// if you are like me that didn't get promises at all read the docs
return new Promise((resolve, reject) => {
setTimeout(() => {
const users = [
{ id: 1, name: 'Pablo' },
{ id: 2, name: 'Pedro' }
]
resolve(users) // this returns users
}, ms)
})
}
(async () => {
const obj = await asyncTimeout(3000)
console.log(obj)
})()

Get the result of a callback function which is having async/await [duplicate]

This is not a realworld problem, I'm just trying to understand how promises are created.
I need to understand how to make a promise for a function that returns nothing, like setTimeout.
Suppose I have:
function async(callback){
setTimeout(function(){
callback();
}, 5000);
}
async(function(){
console.log('async called back');
});
How do I create a promise that async can return after the setTimeout is ready to callback()?
I supposed wrapping it would take me somewhere:
function setTimeoutReturnPromise(){
function promise(){}
promise.prototype.then = function() {
console.log('timed out');
};
setTimeout(function(){
return ???
},2000);
return promise;
}
But I can't think beyond this.
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);
Original Answer from 2014
Usually you'll have a promise library (one you write yourself, or one of the several out there). That library will usually have an object that you can create and later "resolve," and that object will have a "promise" you can get from it.
Then later would tend to look something like this:
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise(); // Note we're not returning `p` directly
}
In a comment on the question, I asked:
Are you trying to create your own promise library?
and you said
I wasn't but I guess now that's actually what I was trying to understand. That how a library would do it
To aid that understanding, here's a very very basic example, which isn't remotely Promises-A compliant: Live Copy
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Very basic promises</title>
</head>
<body>
<script>
(function() {
// ==== Very basic promise implementation, not remotely Promises-A compliant, just a very basic example
var PromiseThingy = (function() {
// Internal - trigger a callback
function triggerCallback(callback, promise) {
try {
callback(promise.resolvedValue);
}
catch (e) {
}
}
// The internal promise constructor, we don't share this
function Promise() {
this.callbacks = [];
}
// Register a 'then' callback
Promise.prototype.then = function(callback) {
var thispromise = this;
if (!this.resolved) {
// Not resolved yet, remember the callback
this.callbacks.push(callback);
}
else {
// Resolved; trigger callback right away, but always async
setTimeout(function() {
triggerCallback(callback, thispromise);
}, 0);
}
return this;
};
// Our public constructor for PromiseThingys
function PromiseThingy() {
this.p = new Promise();
}
// Resolve our underlying promise
PromiseThingy.prototype.resolve = function(value) {
var n;
if (!this.p.resolved) {
this.p.resolved = true;
this.p.resolvedValue = value;
for (n = 0; n < this.p.callbacks.length; ++n) {
triggerCallback(this.p.callbacks[n], this.p);
}
}
};
// Get our underlying promise
PromiseThingy.prototype.promise = function() {
return this.p;
};
// Export public
return PromiseThingy;
})();
// ==== Using it
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise(); // Note we're not returning `p` directly
}
display("Start " + Date.now());
later().then(function() {
display("Done1 " + Date.now());
}).then(function() {
display("Done2 " + Date.now());
});
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
})();
</script>
</body>
</html>
const setTimeoutAsync = (cb, delay) =>
new Promise((resolve) => {
setTimeout(() => {
resolve(cb());
}, delay);
});
We can pass custom 'cb fxn' like this one 👆🏽
One-liner that wraps a promise around setTimeout
await new Promise(r => setTimeout(r, ms))
Example:
async someFunction() {
// Do something
// Wait 2 seconds
await new Promise(r => setTimeout(r, 2000))
// Do something else
}
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'
It uses signals much like browser fetch to handle abort, check the doc for more :)
Implementation:
// Promisify setTimeout
const pause = (ms, cb, ...args) =>
new Promise((resolve, reject) => {
setTimeout(async () => {
try {
resolve(await cb?.(...args))
} catch (error) {
reject(error)
}
}, ms)
})
Tests:
// Test 1
pause(1000).then(() => console.log('called'))
// Test 2
pause(1000, (a, b, c) => [a, b, c], 1, 2, 3).then(value => console.log(value))
// Test 3
pause(1000, () => {
throw Error('foo')
}).catch(error => console.error(error))
This is not an answer to the original question. But, as an original question is not a real-world problem it should not be a problem. I tried to explain to a friend what are promises in JavaScript and the difference between promise and callback.
Code below serves as an explanation:
//very basic callback example using setTimeout
//function a is asynchronous function
//function b used as a callback
function a (callback){
setTimeout (function(){
console.log ('using callback:');
let mockResponseData = '{"data": "something for callback"}';
if (callback){
callback (mockResponseData);
}
}, 2000);
}
function b (dataJson) {
let dataObject = JSON.parse (dataJson);
console.log (dataObject.data);
}
a (b);
//rewriting above code using Promise
//function c is asynchronous function
function c () {
return new Promise(function (resolve, reject) {
setTimeout (function(){
console.log ('using promise:');
let mockResponseData = '{"data": "something for promise"}';
resolve(mockResponseData);
}, 2000);
});
}
c().then (b);
JsFiddle
The simplest way
(async function() {
console.log('1');
await SleepJs(3000);
console.log('2');
} )();
function SleepJs(delay) {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
}
If none of those solutions worked for you, please try this
const asyncTimeout = (ms) => {
// when you make a promise you have to resolve it or reject it
// if you are like me that didn't get promises at all read the docs
return new Promise((resolve, reject) => {
setTimeout(() => {
const users = [
{ id: 1, name: 'Pablo' },
{ id: 2, name: 'Pedro' }
]
resolve(users) // this returns users
}, ms)
})
}
(async () => {
const obj = await asyncTimeout(3000)
console.log(obj)
})()

replace callbacks with async and promises only first is triggered [duplicate]

This is not a realworld problem, I'm just trying to understand how promises are created.
I need to understand how to make a promise for a function that returns nothing, like setTimeout.
Suppose I have:
function async(callback){
setTimeout(function(){
callback();
}, 5000);
}
async(function(){
console.log('async called back');
});
How do I create a promise that async can return after the setTimeout is ready to callback()?
I supposed wrapping it would take me somewhere:
function setTimeoutReturnPromise(){
function promise(){}
promise.prototype.then = function() {
console.log('timed out');
};
setTimeout(function(){
return ???
},2000);
return promise;
}
But I can't think beyond this.
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);
Original Answer from 2014
Usually you'll have a promise library (one you write yourself, or one of the several out there). That library will usually have an object that you can create and later "resolve," and that object will have a "promise" you can get from it.
Then later would tend to look something like this:
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise(); // Note we're not returning `p` directly
}
In a comment on the question, I asked:
Are you trying to create your own promise library?
and you said
I wasn't but I guess now that's actually what I was trying to understand. That how a library would do it
To aid that understanding, here's a very very basic example, which isn't remotely Promises-A compliant: Live Copy
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Very basic promises</title>
</head>
<body>
<script>
(function() {
// ==== Very basic promise implementation, not remotely Promises-A compliant, just a very basic example
var PromiseThingy = (function() {
// Internal - trigger a callback
function triggerCallback(callback, promise) {
try {
callback(promise.resolvedValue);
}
catch (e) {
}
}
// The internal promise constructor, we don't share this
function Promise() {
this.callbacks = [];
}
// Register a 'then' callback
Promise.prototype.then = function(callback) {
var thispromise = this;
if (!this.resolved) {
// Not resolved yet, remember the callback
this.callbacks.push(callback);
}
else {
// Resolved; trigger callback right away, but always async
setTimeout(function() {
triggerCallback(callback, thispromise);
}, 0);
}
return this;
};
// Our public constructor for PromiseThingys
function PromiseThingy() {
this.p = new Promise();
}
// Resolve our underlying promise
PromiseThingy.prototype.resolve = function(value) {
var n;
if (!this.p.resolved) {
this.p.resolved = true;
this.p.resolvedValue = value;
for (n = 0; n < this.p.callbacks.length; ++n) {
triggerCallback(this.p.callbacks[n], this.p);
}
}
};
// Get our underlying promise
PromiseThingy.prototype.promise = function() {
return this.p;
};
// Export public
return PromiseThingy;
})();
// ==== Using it
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise(); // Note we're not returning `p` directly
}
display("Start " + Date.now());
later().then(function() {
display("Done1 " + Date.now());
}).then(function() {
display("Done2 " + Date.now());
});
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
})();
</script>
</body>
</html>
const setTimeoutAsync = (cb, delay) =>
new Promise((resolve) => {
setTimeout(() => {
resolve(cb());
}, delay);
});
We can pass custom 'cb fxn' like this one 👆🏽
One-liner that wraps a promise around setTimeout
await new Promise(r => setTimeout(r, ms))
Example:
async someFunction() {
// Do something
// Wait 2 seconds
await new Promise(r => setTimeout(r, 2000))
// Do something else
}
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'
It uses signals much like browser fetch to handle abort, check the doc for more :)
Implementation:
// Promisify setTimeout
const pause = (ms, cb, ...args) =>
new Promise((resolve, reject) => {
setTimeout(async () => {
try {
resolve(await cb?.(...args))
} catch (error) {
reject(error)
}
}, ms)
})
Tests:
// Test 1
pause(1000).then(() => console.log('called'))
// Test 2
pause(1000, (a, b, c) => [a, b, c], 1, 2, 3).then(value => console.log(value))
// Test 3
pause(1000, () => {
throw Error('foo')
}).catch(error => console.error(error))
This is not an answer to the original question. But, as an original question is not a real-world problem it should not be a problem. I tried to explain to a friend what are promises in JavaScript and the difference between promise and callback.
Code below serves as an explanation:
//very basic callback example using setTimeout
//function a is asynchronous function
//function b used as a callback
function a (callback){
setTimeout (function(){
console.log ('using callback:');
let mockResponseData = '{"data": "something for callback"}';
if (callback){
callback (mockResponseData);
}
}, 2000);
}
function b (dataJson) {
let dataObject = JSON.parse (dataJson);
console.log (dataObject.data);
}
a (b);
//rewriting above code using Promise
//function c is asynchronous function
function c () {
return new Promise(function (resolve, reject) {
setTimeout (function(){
console.log ('using promise:');
let mockResponseData = '{"data": "something for promise"}';
resolve(mockResponseData);
}, 2000);
});
}
c().then (b);
JsFiddle
The simplest way
(async function() {
console.log('1');
await SleepJs(3000);
console.log('2');
} )();
function SleepJs(delay) {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
}
If none of those solutions worked for you, please try this
const asyncTimeout = (ms) => {
// when you make a promise you have to resolve it or reject it
// if you are like me that didn't get promises at all read the docs
return new Promise((resolve, reject) => {
setTimeout(() => {
const users = [
{ id: 1, name: 'Pablo' },
{ id: 2, name: 'Pedro' }
]
resolve(users) // this returns users
}, ms)
})
}
(async () => {
const obj = await asyncTimeout(3000)
console.log(obj)
})()

Javascript understanding promises

I've been using async with NodeJS, but I'm trying to understand the basic of promises in JavaScript and I'm having issues with it.
I created the code below for testing.
function first() {
// Simulate a code delay
return new Promise(function(resolve) {
setTimeout(function() {
console.log(1);
}, 500);
});
}
function second() {
console.log(2);
}
first().then(second());
It should print '1' first, but it is printing '2' in the console first. Why is it happening?
Thanks
Two things:
first().then(second()) invokes second() immediately and passes its return value as the argument to then. You want to pass in the function itself, not its return value:
first().then(second); // no parens on second
And you're never resolving the first promise:
return new Promise(function(resolve) {
setTimeout(function() {
console.log(1);
resolve(); // <-- without this the promise is never resolved
}, 500);
}
);
With those issues addressed it works as you'd intended:
function first() {
// Simulate a code delay
return new Promise(function(resolve) {
setTimeout(function() {
console.log(1);
resolve(); // <-- without this the promise is never resolved
}, 500);
});
}
function second() {
console.log(2);
}
first().then(second);
There are a 2 problems in your code :
Your function first() returns a promise, but it isn't a promise in itself, so you cannot use the .then attribute on it.
You're not resolving anything in your 1st promise, you're just console.logging something, which is not something you can "wait" for.
What you're looking for is more something like this :
let first = new Promise((resolve) => {
setTimeout( function() {
console.log("1");
resolve(true); //you return your response, for example a boolean
}, 500)
})
function second(){
console.log("2");
}
first.then(response => {
//this is usually where you do something with the response
second();
});
Use Async Await
(as ray write) In new Promise() You must return resolve() or reject()
You can also use await:
function first() {
return new Promise(res => {
console.log(1);
return setTimeout(res, 500);
})
}
function second() {
console.log(2);
}
(async () => {
await first();
second();
})();

Am I nesting the function correctly?

I have little doubt, I have let say 5 or more js functions each calls C# functions Via Ajax calls doing various task like(fetching data from the database, validating it, calculations, saving it back to The database etc).
I am calling the function using Nested jQuery $.when(function1()).then(function2()) and so on. I want function one to fully complete before two and two before the third one and so one.... there are some dependency between these functions...
My Code example Like: (My Approach)
$(document).ready(function () {
$.when(one()).then(
$.when(two()).then(
$.when(three()).done(alert('three done')).then(
$.when(four()).then(
five()
).done(alert('All Finished Up'))
)
)
)
});
function one() //eg List 1000 records from Db
function two() //Update Some
function three() //validate Changes
function four() //save Changes
function five() // Some Other Task then
Now my question is simple I know rather than calling sequentially like
$(document).ready(function(){
one();
two();
three();
four();
five();
});
I used above nesting pattern (My Approach)... Is that good approach coding-wise and performance wise? Is there any more efficient approach than this?
Alternatively, you can run all of those calls in parallel to get a faster return
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function one(mode) {
await sleep(1000);
console.log(`${mode} : one() finished`);
}
async function two(mode) {
await sleep(3000);
console.log(`${mode} : two() finished`);
}
async function three(mode) {
await sleep(3000);
console.log(`${mode} : three() finished`);
}
async function four(mode) {
await sleep(4000);
console.log(`${mode} : four() finished`);
}
async function five(mode) {
await sleep(5000);
console.log(`${mode} : five() finished`);
}
async function runInSerial() {
await one('serial');
await two('serial');
await three('serial');
await four('serial');
await five('serial');
}
async function runInParallel() {
await Promise.all([
one('parallel'),
two('parallel'),
three('parallel'),
four('parallel'),
five('parallel'),
]);
}
$(document).ready(function () {
runInSerial().then(() => console.log('serial : all done!'));
runInParallel().then(() => console.log('parallel : all done!'));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
There is: if you want to only support browsers that support ES6, you can simply use the async/await syntax, as the jQuery Promises return a native Promise object anyway:
// Dummy async operation
function sleep(t) {
return new Promise(resolve => setTimeout(resolve, t));
}
$(document).ready(function() {
run().then(() => console.log('all done'));
});
// Run all your async functions sequentially
async function run() {
await one();
console.log('one done');
await two();
console.log('two done');
}
async function one() {
return sleep(500);
}
async function two() {
return sleep(500);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Categories

Resources