Promise.map function .then() block getting null before nested promise resolves - javascript

I'm kicking off a nested promise mapping and seeing the outer .then() block print a null result before the resolve in the function is called.
I feel like I must be messing up the syntax somehow. I've made this stripped down example:
const Promise = require('bluebird');
const topArray = [{outerVal1: 1,innerArray: [{innerVal1: 1,innerVal2: 2}, {innerVal1: 3,innerVal2: 4}]},{outerVal2: 2,innerArray: [{innerVal1: 5, innerVal2: 6 }, {innerVal1: 7,innerVal2: 8 }]}] ;
promiseWithoutDelay = function (innerObject) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("promiseWithDelay" ,innerObject);
let returnVal = {}
returnVal.innerVal1 = innerObject.innerVal1;
returnVal.innerVal2 = innerObject.innerVal2;
returnVal.delay = false;
return resolve(returnVal);
}, 0);
})
}
promiseWithDelay = function (innerObject) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("promiseWithDelay" ,innerObject);
let returnVal = {}
returnVal.innerVal1 = innerObject.innerVal1;
returnVal.innerVal2 = innerObject.innerVal2;
returnVal.delay = true;
return resolve(returnVal);
}, 3000);
})
}
test1 = function () {
let newArray = [];
let newArrayIndex = 0;
Promise.map(topArray, function (outerObject) {
Promise.map(outerObject.innerArray, function (innerObject) {
Promise.all([
promiseWithoutDelay(innerObject),
promiseWithDelay(innerObject)
])
.then(function (promiseResults) {
newArray[newArrayIndex++] = {result1: promiseResults[1], result2: promiseResults[2]}
})
})
})
.then(function () {
return newArray;
})
}
var result = test1();
console.log("got result ",result);
What I'm trying to do is loop over an outer array that has some values that I need.
These values include a nested inner array that I must also loop over to get some values.
In the inner loop I pass the outer and inner values to promise functions in a Promise.all.
When the promise functions resolve they get assigned to a return object.
It seems to be working fine except for one of the promise functions sometimes has a delay as it's doing some calculations.
When this happens it is left out of the return value because it hasn't resolved yet.
Shouldn't it wait until the inner loop with Promise.all resolves before it returns from the outer loop?
Can you point me in the right direction?
EDIT: Ended up with this solution based on #Thomas's suggestion:
test1 = function(){
return Promise.map(topArray, function(outerObject){
let oVal = outerObject.outerVal;
return Promise.map(outerObject.innerArray, function(innerObject){
innerObject.oVal = oVal;
return Promise.all([ promiseWithDelay(innerObject), promiseWithoutDelay(innerObject)])
.then(function(results) {
return { result1: results[0], result2: results[1], delay: results[2] } ;
})
})
}).reduce(function(newArray, arr){
return newArray.concat(arr);
}, []);
}

I'm not entirely sure I get your problem from your stripped down example, but I think what you want to do here is this:
test1 = function(){
return Promise.map(topArray, function(outerObject){
return Promise.all(outerObject.innerArray)
}).reduce(function(newArray, arr){
return newArray.concat(arr);
}, []);
}

Related

How can I chain functions asynchronously using JavaScript?

I was asked to create such an object called foo that can chain the function log and wait.
For example:
foo.log('breakfast').wait(3000).log('lunch').wait(3000).log('dinner');
In this scenario it prints breakfast first, waits 3 seconds, prints lunch, and then after 3 seconds it prints dinner.
I tried something like this, but it doesn't work. What did I miss?
var foo = {
log: function(text){
console.log(text);
return foo;
},
wait: function(time) {
setTimeout(function() {
return foo;
}, time);
}
}
foo.log('breakfast').wait(3000).log('lunch').wait(3000).log('dinner');
It's always better to use promises. An implementation of this functionality could be;
class Foo {
constructor(){
this.promise = Promise.resolve();
}
log(txt){
this.promise = this.promise.then(_ => console.log(txt))
return this;
}
wait(ms){
this.promise = this.promise.then(_ => new Promise(v => setTimeout(v,ms)));
return this;
}
}
var foo = new Foo();
foo.log("happy").wait(1000).log("new").wait(1000).log("year");
For the record, Redu's excellent answer without the class sugar.
See also
const foo = {
promise: Promise.resolve(),
log(txt) {
this.promise.then(_ => console.log(txt));
return this;
},
wait(ms) {
this.promise = this.promise.then(_ => new Promise(v => setTimeout(v, ms)));
return this;
}
};
// OR
const Foo = (defaultMs = 1000) => {
let promised = Promise.resolve();
return {
log(txt) {
promised.then(_ => console.log(txt));
return this;
},
wait: function(ms) {
promised = promised.then( _=>
new Promise( rs => setTimeout(rs, ms || defaultMs) ) );
return this;
}
};
};
foo.log("Happy").wait(1000).log("new").wait(1000).log("year");
Foo().wait(3000)
.log(`** From Foo ;)`).log(`Happy`).wait().log("new").wait().log("year");
Place the call to wait inside the previous one, and as the last item, like a recursive function.
meals=['breakfast','elevenses','lunch','afternoon tea','dinner','supper'];
c=0;
wait=t=>{setTimeout(function() {
if (c<meals.length) document.write(meals[c++],'<br>');wait(500);
}, t);}
wait(500);
I was inspired by #James 's solution, which is partially wrong because the log messages are in the reverse order, but he does not use Promises. I still think that #Redu 's solution should be the accepted one (after all if you can use Promises, that is perfect), but this one is interesting too in my opinion:
const foo1 = {
incrementalTimeout: 0,
nextActions: [],
log(text) {
const textLog = () => { console.log(text); };
if (this.incrementalTimeout == 0)
textLog();
else
this.nextActions.push(textLog);
return this;
},
wait(time) {
let newObj = {...this, incrementalTimeout: this.incrementalTimeout + time, nextActions: []};
setTimeout(() => { newObj.nextActions.forEach((action) => action()); } , newObj.incrementalTimeout);
return newObj;
}
}
foo1.log('breakfast').wait(1000).log('lunch').wait(3000).log('dinner');
The idea is that I do not immediately log text but I push a lambda with the console.log in an array that is going to be called after the correct timeout expires.
I run all the log and wait operations one after the other, but I keep track of the seconds to wait before executing the actions. Every time a new wait is called, the incrementalTimeout is increased by time. To keep the nextActions belonging to different time frames separated, I return a newObj every time, more or less like #James does.
Shorter, within Promise (not recommended).
Promise.prototype.log = function(txt) {
return this.then(() => console.log(txt))
}
Promise.prototype.wait = function(ms) {
return this.then(() => new Promise(res => setTimeout(res, ms)))
}
var foo = Promise.resolve()
foo.log('breakfast').wait(3000).log('lunch').wait(3000).log('dinner')
You can do it without promises:
const foo = {
log(text) {
return {...foo, start: () => {
this.start();
console.log(text);
}};
},
wait(time) {
return {...foo, start: () => {
setTimeout(this.start, time);
}};
},
start() {}
};
foo.log('breakfast').wait(3000).log('lunch').wait(3000).log('dinner').start();
The functions foo.log() and foo.wait() return immediately, returning a modified clone of foo. A clone is made using {...foo}, but with the start() function modified so that it calls the caller's this.start() followed by the new operation. When the chain is complete you call start() to start the actions.

What's the alternative to the reduce pattern with Promises when dealing with recursion?

Note: I cannot use async.
I like to use the reduce pattern in cases where I need to run over an array and execute the same function on its members and return a promise, like so:
function get_count() {
return new Promise(function(resolve, reject) {
resolve(3);
});
}
function recursively_execute(data) {
return new Promise(function(resolve, reject) {
resolve(data);
});
}
function reduce_promise_pattern() {
const get_batch_run_count = get_count();
const batch_process = get_batch_run_count.then((count_value) => {
const run_count = new Array(count_value).fill('batch');
function recursive_function(data) {
console.log('Running batch!');
return recursively_execute(data).then(() => {
return data;
});
}
return run_count.reduce((previous_promise) => {
return previous_promise.then((previous_response) => {
test_data = {
'test': 1
};
return recursive_function(test_data);
})
}, Promise.resolve())
});
return batch_process;
}
This will run 3 times because of the run_count which basically builds an array of 3 items. Although it works, this feels like a hack to me.
This approach works when my list is already pre-defined with unique items and these items, well, individually are used inside that reduce as data that is built upon for example, if I have 3 steps to go through, these 3 steps are all unique and each step's data will be used within that one run...but in my case? I'm just tricking the system to think these are different items.
What is the alternative to this?
You reached the limits of Promise chains, although they work they ain't readable. That's why async / await was introduced to handle exactly these usecases, with them you can just halt all kinds of (nested) loops without having to maintain promises for each:
async function reducePromisePattern() {
for(let i = await getCount(); i >= 0; i--) {
await recursiveFunction({'test': 1 });
}
}
If you can't use / transpile async, you could still write some small helpers to do the looping for you, e.g.:
function loopAsync(times, fn) {
function task() {
times--;
if(times <= 0) return;
return fn().then(task);
}
return Promise.resolve().then(task);
}
function reducePromisePattern() {
return getCount().then(function(count) {
return asyncLoop(count, function() {
return recursiveFunction({ test: 1 });
});
});
}
Here are two options without nesting functions in one another. The first one simply uses a for-loop while the second function uses a recursive solution. The last argument of both solutions is optional and should only be used if you want to pass the return data forward from one run to the next (similar to reduce).
const sleep = () => new Promise(resolve => setTimeout(resolve, Math.random() * 1500 + 500));
// solution #1 - for-loop
function times1(n, callback, init) {
var promise = Promise.resolve(init);
for (; n > 0; --n) {
promise = promise.then(val => callback(val));
}
return promise;
}
// example usage
times1(3, n => {
console.log("solution #1 -", n);
return sleep().then(() => n + 1);
}, 0);
// solution #2 - recursive
function times2(n, callback, init) {
var promise = Promise.resolve(init);
if (n <= 0) return promise;
return promise.then(val => times2(n - 1, callback, callback(val)));
}
// example usage
times2(3, n => {
console.log("solution #2 -", n);
return sleep().then(() => n + 1);
}, 0);

Use `Promise.resolve` For Ordering Multi-function

I Have 3 Function and each one work with Promise.resolve Invidualy,
How Can use Promise.resolve For All?, When I Call All Functions, Those Aren't Ordered
function sendAllText(msg, opts) {
if (locale.keyboards[msg.text].text) {
var i,j,tempstring, promise;
promise = Promise.resolve();
for (i=0,j=locale.keyboards[msg.text].text.length; i<j; i++) {
tempstring = locale.keyboards[msg.text].text[i];
promise = promise.then(bot.sendMessage.bind(bot,msg.chat.id, tempstring, opts));
}
}
}
function sendAllPhoto(msg, opts) {
if (locale.keyboards[msg.text].photo) {
var i,j,tempstring, promise;
promise = Promise.resolve();
for (i=0,j=locale.keyboards[msg.text].photo.length; i<j; i++) {
tempstring = locale.keyboards[msg.text].photo[i];
promise = promise.then(bot.sendPhoto.bind(bot,msg.chat.id, tempstring, opts));
}
}
}
function sendAllVideo(msg, opts) {
if (locale.keyboards[msg.text].video) {
var i,j,tempstring, promise;
promise = Promise.resolve();
for (i=0,j=locale.keyboards[msg.text].video.length; i<j; i++) {
tempstring = locale.keyboards[msg.text].video[i];
promise = promise.then(bot.sendVideo.bind(bot,msg.chat.id, tempstring, opts));
}
}
}
When I call Functions, My Data is not Ordered, I'm Using Node telegram bot Api
bot.onText(/\/love/, function onLoveText(msg) {
const opts = {
reply_to_message_id: msg.message_id,
reply_markup: JSON.stringify({
keyboard: [
['Yes, you are the bot of my life ❤'],
['No, sorry there is another one...']
]
})
};
sendAllText(msg, opts);
sendAllPhoto(msg, opts);
sendAllVideo(msg, opts);
});
At the end of each of the three functions, right after their loops, add:
return promise;
Also make sure you define the promise variable at the start of the function, so it also is defined when the if condition is not true.
For example, in the first function:
function sendAllText(msg, opts) {
var promise = Promise.resolve(); // <----
if (locale.keyboards[msg.text].text) {
var i,j,tempstring;
for (i=0,j=locale.keyboards[msg.text].text.length; i<j; i++) {
tempstring = locale.keyboards[msg.text].text[i];
promise = promise.then(bot.sendMessage.bind(bot,msg.chat.id, tempstring, opts));
}
}
return promise; // <-----
}
Then in the last piece of code, chain your promises:
sendAllText(msg, opts)
.then(sendAllPhoto.bind(null, msg, opts))
.then(sendAllVideo.bind(null, msg, opts));
You can use $q.all, The $q.all() method takes either an object or an array of promises and waits for all of them to resolve() or one of them to reject() and then executes the provided callback function. The values returned from the resolve function are provided depending on the way you give the promises to all().
Example -
var promises = [sendAllText(), sendAllPhoto(), sendAllVideo()];
$q.all(promises).then((values) => {
console.log(values[0]); // value Text
console.log(values[1]); // value Photo
console.log(values[2]); // value Video
});

How to make compound promises execute asynchronously [duplicate]

I want to process a number of promises in Sequence. I have a working piece of code below but I'm wondering if I have over complicated the chaining of promises. I seem to be creating a great deal of new closures and I'm scratching my head wondering if I'm missing something.
Is there a better way to write this function:
'use strict';
addElement("first")
.then(x => {return addElement("second")})
.then(x => { return addElement("third")})
.then(x => { return addElement("fourth")})
function addElement(elementText){
var myPromise = new Promise(function(resolve,reject){
setTimeout(function(){
var element=document.createElement('H1');
element.innerText = `${elementText} ${Date.now()}`;
document.body.appendChild(element);
resolve();
}, Math.random() * 2000);
});
return myPromise;
}
#TheToolBox has a nice answer for you.
Just for fun, I'm going to show you an alternative technique that uses generators that gets its inspiration from coroutines.
Promise.prototype.bind = Promise.prototype.then;
const coro = g => {
const next = x => {
let {done, value} = g.next(x);
return done ? value : value.bind(next);
}
return next();
}
Using that, your code will look like this
const addElement = elementText =>
new Promise(resolve => {
setTimeout(() => {
var element = document.createElement('H1');
element.innerText = `${elementText} ${Date.now()}`;
document.body.appendChild(element);
resolve();
}, Math.random() * 2000);
});
coro(function* () {
yield addElement('first');
yield addElement('second');
yield addElement('third');
yield addElement('fourth');
}());
There's some pretty interesting things you can do using generators with promises. They're not immediately evident here because your addElement promise doesn't resolve any actual values.
If you actually resolve some values, you could do something like
// sync
const appendChild = (x,y) => x.appendChild(y);
// sync
const createH1 = text => {
var elem = document.createElement('h1');
elem.innerText = `${text} ${Date.now()}`;
return elem;
};
// async
const delay = f =>
new Promise(resolve => {
setTimeout(() => resolve(f()), Math.random() * 2000);
});
// create generator; this time it has a name and accepts an argument
// mix and match sync/async as needed
function* renderHeadings(target) {
appendChild(target, yield delay(() => createH1('first')));
appendChild(target, yield delay(() => createH1('second')));
appendChild(target, yield delay(() => createH1('third')));
appendChild(target, yield delay(() => createH1('fourth')));
}
// run the generator; set target to document.body
coro(renderHeadings(document.body));
Worth noting, createH1 and appendChild are synchronous functions. This approach effectively allows you to chain normal functions together and blur the lines between what is sync and what is async. It also executes/behaves exactly like the code you originally posted.
So yeah, this last code example might be slightly more interesting.
Lastly,
One distinct advantage the coroutine has over the .then chaining, is that all of the resolved promises can be accessed inside the same scope.
Compare .then chains ...
op1()
.then(x => op2(x))
.then(y => op3(y)) // cannot read x here
.then(z => lastOp(z)) // cannot read x or y here
to the coroutine ...
function* () {
let x = yield op1(); // can read x
let y = yield op2(); // can read x and y here
let z = yield op3(); // can read x, y, and z here
lastOp([x,y,z]); // use all 3 values !
}
Of course there are workarounds for this using promises, but oh boy does it get ugly fast...
If you are interested in using generators in this way, I highly suggest you checkout the co project.
And here's an article, Callbacks vs Coroutines, from the creator of co, #tj.
Anyway, I hope you had fun learning about some other techniques ^__^
I am not sure why others left out a simple way out, you could simply use an array and reduce method
let promise, inputArray = ['first', 'second', 'third', 'fourth'];
promise = inputArray.reduce((p, element) => p.then(() => addElement(element)), Promise.resolve());
Your code looks close to the best you can get here. Promises can be a strange structure to get used to, especially as writing promis-ified code can often end up embedding a function in another function. As you can see here, this is a pretty common phrasing to use. There are only two stylistic changes that could possibly be made. Firstly, myPromise is unnecessary and only serves to add a confusing extra line of code. It's simpler just to return the promise directly. Secondly, you can use function binding to simplify your calls at the beginning. It may not be inside the function itself, but it does eliminate several closures. Both changes are shown below:
'use strict';
addElement("first")
.then(addElement.bind(null,"second"))
.then(addElement.bind(null,"third"))
.then(addElement.bind(null,"fourth"))
function addElement(elementText){
return new Promise(function(resolve,reject){
setTimeout(function(){
var element=document.createElement('H1');
element.innerText = `${elementText} ${Date.now()}`;
document.body.appendChild(element);
resolve();
}, Math.random() * 2000);
});
}
It's worth pointing out that, if you were willing to restructure a bit, a slightly more attractive design would take form:
'use strict';
var myWait = waitRand.bind(null,2000);
myWait
.then(addElement.bind(null, "first"))
.then(myWait)
.then(addElement.bind(null, "second"))
.then(myWait)
.then(addElement.bind(null, "third"))
function waitRand(millis) {
return new Promise((resolve, reject) => {
setTimeout(resolve, Math.random() * millis);
}
}
function addElement(elementText) {
var element = document.createElement('h1');
element.innerText = `${elementText} ${Date.now()}`;
document.body.appendChild(element);
}
This trades length of promise chain for clarity, as well as having slightly fewer nested levels.
You could simplify the use of your function by making addElement() return a function instead so it can be directly inserted into .then() handlers without having to create the anonymous function:
'use strict';
addElement("first")()
.then(addElement("second"))
.then(addElement("third"))
.then(addElement("fourth"))
function addElement(elementText){
return function() {
return new Promise(function(resolve){
setTimeout(function(){
var element=document.createElement('H1');
element.innerText = `${elementText} ${Date.now()}`;
document.body.appendChild(element);
resolve();
}, Math.random() * 2000);
});
}
}
There's not much to be done with regard to the number of closures. Nesting of functions is just something you get used to with js, and the code in the question really isn't that bad.
As others have said, writing addElement() to return a function makes for a neater main promise chain.
Going slightly further, you might consider writing the returned function with an inner promise chain, allowing the (slight) separation of promise resolution from DOM element insertion. This creates no more and no less closures, but is syntactically neater, in particular allowing you to write setTimeout(resolve, Math.random() * 2000);.
'use strict';
addElement("first")
.then(addElement("second"))
.then(addElement("third"))
.then(addElement("fourth"));
function addElement(elementText) {
return function() {
return new Promise(function(resolve, reject) {
setTimeout(resolve, Math.random() * 2000);
}).then(function() {
var element = document.createElement('H1');
document.body.appendChild(element);
element.innerText = `${elementText} ${Date.now()}`;
});
};
}
Maybe it's just me but I find this much more pleasing on the eye, albeit at the cost of an additional .then(), hence an additional promise, per addElement().
Note: If you needed to resolve the promise with a value, you are still afforded the opportunity to do so by returning a value from the chained then's callback.
Going even further, if you want the inserted elements to appear in the demanded order, not the order determined by promise settlement, then you can create/insert elements synchronously, and populate them asynchronously :
function addElement(elementText) {
var element = document.createElement('H1');
document.body.appendChild(element);
return function() {
return new Promise(function(resolve, reject) {
setTimeout(resolve, Math.random() * 2000);
}).then(function() {
element.innerText = `${elementText} ${Date.now()}`;
});
};
}
All that was necessary was to move two lines within addElement(), to change the timing of the insertions whilst leaving the element.innerText = ... line where it was. This is possible whether or not you opt for the inner promise chain.
I wrote two methods here :
Sequence = {
all( steps ) {
var promise = Promise.resolve(),
results = [];
const then = i => {
promise = promise.then( () => {
return steps[ i ]().then( value => {
results[ i ] = value;
} );
} );
};
steps.forEach( ( step, i ) => {
then( i );
} );
return promise.then( () => Promise.resolve( results ) );
},
race( steps ) {
return new Promise( ( resolve, reject ) => {
var promise = Promise.reject();
const c = i => {
promise = promise.then( value => {
resolve( value );
} ).catch( () => {
return steps[ i ]();
} );
};
steps.forEach( ( step, i ) => {
c( i );
} );
promise.catch( () => {
reject();
} );
} );
}
};
Sequence.all will run functions in a sequence until all promises in arguments are resolved. And return another Promise object with arguments as an array filled with all resolved values in sequence.
Sequence.all( [ () => {
return Promise.resolve( 'a' );
}, () => {
return Promise.resolve( 'b' );
} ] ).then( values => {
console.log( values ); // output [ 'a', 'b' ]
} );
Sequence.race will run functions in a sequence and stop running while one promise object been resolved.
Sequence.race( [ () => {
return Promise.reject( 'a' );
}, () => {
return Promise.resolve( 'b' );
}, () => {
return Promise.resolve( 'c' );
} ] ).then( values => {
console.log( values ); // output [ 'a' ]
} );

Concept - Distilling how a promise works?

I've looked at many implementations and they all look so different I can't really distill what the essence of a promise is.
If I had to guess it is just a function that runs when a callback fires.
Can someone implement the most basic promise in a few lines of code w/ out chaining.
For example from this answer
Snippet 1
var a1 = getPromiseForAjaxResult(ressource1url);
a1.then(function(res) {
append(res);
return a2;
});
How does the function passed to then know when to run.
That is, how is it passed back to the callback code that ajax fires on completion.
Snippet 2
// generic ajax call with configuration information and callback function
ajax(config_info, function() {
// ajax completed, callback is firing.
});
How are these two snippets related?
Guess:
// how to implement this
(function () {
var publik = {};
_private;
publik.then = function(func){
_private = func;
};
publik.getPromise = function(func){
// ??
};
// ??
}())
Fundamentally, a promise is just an object that has a flag saying whether it's been settled, and a list of functions it maintains to notify if/when it is settled. Code can sometimes say more than words, so here's a very basic, not-real-world example purely indended to help communicate the concepts:
// See notes following the code for why this isn't real-world code
function Promise() {
this.settled = false;
this.settledValue = null;
this.callbacks = [];
}
Promise.prototype.then = function(f) {
if (this.settled) {
f(this.settledValue); // See notes 1 and 2
} else {
this.callbacks.push(f);
}
// See note 3 about `then`
// needing a return value
};
Promise.prototype.settle = function(value) { // See notes 4 and 5
var callback;
if (!this.settled) {
this.settled = true;
this.settledValue = value;
while (this.callbacks.length) {
callback = this.callbacks.pop();
callback(this.settledValue); // See notes 1 and 2
}
}
};
So the Promise holds the state, and the functions to call when the promise is settled. The act of settling the promise is usually external to the Promise object itself (although of course, that depends on the actual use, you might combine them — for instance, as with jQuery's ajax [jqXHR] objects).
Again, the above is purely conceptual and missing several important things that must be present in any real-world promises implementation for it to be useful:
then and settle should always call the callback asynchronously, even if the promise is already settled. then should because otherwise the caller has no idea whether the callback will be async. settle should because the callbacks shouldn't run until after settle has returned. (ES2015's promises do both of these things. jQuery's Deferred doesn't.)
then and settle should ensure that failure in the callback (e.g., an exception) is not propagated directly to the code calling then or settle. This is partially related to #1 above, and more so to #3 below.
then should return a new promise based on the result of calling the callback (then, or later). This is fairly fundamental to composing promise-ified operations, but would have complicated the above markedly. Any reasonable promises implementation does.
We need different types of "settle" operation: "resolve" (the underlying action succeeded) and "reject" (it failed). Some use cases might have more states, but resolved and rejected are the basic two. (ES2015's promises have resolve and reject.)
We might make settle (or the separate resolve and reject) private in some way, so that only the creator of the promise can settle it. (ES2015 promises — and several others — do this by having the Promise constructor accept a callback that receives resolve and reject as parameter values, so only code in that callback can resolve or reject [unless code in the callback makes them public in some way].)
Etc., etc.
Can someone implement the most basic promise in a few lines?
Here it is:
function Promise(exec) {
// takes a function as an argument that gets the fullfiller
var callbacks = [], result;
exec(function fulfill() {
if (result) return;
result = arguments;
for (let c;c=callbacks.shift();)
c.apply(null, arguments);
});
this.addCallback = function(c) {
if (result)
c.apply(null, result)
else
callbacks.push(c);
}
}
Additional then with chaining (which you will need for the answer):
Promise.prototype.then = function(fn) {
return new Promise(fulfill => {
this.addCallback((...args) => {
const result = fn(...args);
if (result instanceof Promise)
result.addCallback(fulfill);
else
fulfill(result);
});
});
};
How are these two snippets related?
ajax is called from the getPromiseForAjaxResult function:
function getPromiseForAjaxResult(ressource) {
return new Promise(function(callback) {
ajax({url:ressource}, callback);
});
}
I've implement one in ES7. With chaining, it's 70 lines, if that counts as few. I think State Machine is the right paradigm for implementing promises. Resulting code is more understandable than lots of ifs IMHO. Described fully in this article.
Here's the code:
const states = {
pending: 'Pending',
resolved: 'Resolved',
rejected: 'Rejected'
};
class Nancy {
constructor(executor) {
const tryCall = callback => Nancy.try(() => callback(this.value));
const laterCalls = [];
const callLater = getMember => callback => new Nancy(resolve => laterCalls.push(() => resolve(getMember()(callback))));
const members = {
[states.resolved]: {
state: states.resolved,
then: tryCall,
catch: _ => this
},
[states.rejected]: {
state: states.rejected,
then: _ => this,
catch: tryCall
},
[states.pending]: {
state: states.pending,
then: callLater(() => this.then),
catch: callLater(() => this.catch)
}
};
const changeState = state => Object.assign(this, members[state]);
const apply = (value, state) => {
if (this.state === states.pending) {
this.value = value;
changeState(state);
for (const laterCall of laterCalls) {
laterCall();
}
}
};
const getCallback = state => value => {
if (value instanceof Nancy && state === states.resolved) {
value.then(value => apply(value, states.resolved));
value.catch(value => apply(value, states.rejected));
} else {
apply(value, state);
}
};
const resolve = getCallback(states.resolved);
const reject = getCallback(states.rejected);
changeState(states.pending);
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
static resolve(value) {
return new Nancy(resolve => resolve(value));
}
static reject(value) {
return new Nancy((_, reject) => reject(value));
}
static try(callback) {
return new Nancy(resolve => resolve(callback()));
}
}
Here's a light-weight promise implementation, called 'sequence', which I use in my day-to-day work:
(function() {
sequence = (function() {
var chained = [];
var value;
var error;
var chain = function(func) {
chained.push(func);
return this;
};
var execute = function(index) {
var callback;
index = typeof index === "number" ? index : 0;
if ( index >= chained.length ) {
chained = [];
return true;
}
callback = chained[index];
callback({
resolve: function(_value) {
value = _value;
execute(++index);
},
reject: function(_error) {
error = _error;
execute(++index);
},
response: {
value: value,
error: error
}
});
};
return {
chain: chain,
execute: execute
};
})();
})();
Once initialized, you can use sequence in the following way:
sequence()
.chain(function(seq) {
setTimeout(function() {
console.log("func A");
seq.resolve();
}, 2000);
})
.chain(function(seq) {
setTimeout(function() {
console.log("func B");
}, 1000)
})
.execute()
To enable the actual chaining, you need to call the resolve() function of the seq object, which your callbacks must use as an argument.
Sequence exposes two public methods:
chain - this method simply pushes your callbacks to a private array
execute - this method uses recursion to enable the proper sequential execution of your callbacks. It basically executes your callbacks in the order you've chained them by passing the seq object to each of them. Once the current callback is resolved/rejected, the next callback is executed.
The 'execute' method is where the magic happens. It passes the 'seq' object to all of your callbacks. So when you call seq.resolve() or seq.reject() you'll actually call the next chained callback.
Please, note that this implementation stores a response from only the previously executed callback.
For more examples and documentation, please refer to:
https://github.com/nevendyulgerov/sequence
Here is a simple Promise implementation that works for me.
function Promise(callback) {
this._pending = [];
this.PENDING = "pending";
this.RESOLVED = "resolved";
this.REJECTED = "rejected";
this.PromiseState = this.PENDING;
this._catch = function (error) {
console.error(error);
};
setTimeout(function () {
try {
callback.call(this, this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}.bind(this), 0)
};
Promise.prototype.resolve = function (object) {
if (this.PromiseState !== this.PENDING) return;
while (this._pending.length > 0) {
var callbacks = this._pending.shift();
try {
var resolve = callbacks.resolve;
if (resolve instanceof Promise) {
resolve._pending = resolve._pending.concat(this._pending);
resolve._catch = this._catch;
resolve.resolve(object);
return resolve;
}
object = resolve.call(this, object);
if (object instanceof Promise) {
object._pending = object._pending.concat(this._pending);
object._catch = this._catch;
return object;
}
} catch (error) {
(callbacks.reject || this._catch).call(this, error);
return;
}
}
this.PromiseState = this.RESOLVED;
return object;
};
Promise.prototype.reject = function (error) {
if (this.PromiseState !== this.PENDING) return;
this.PromiseState = this.REJECTED;
try {
this._catch(error);
} catch (e) {
console.error(error, e);
}
};
Promise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = onFulfilled || function (result) {
return result;
};
this._catch = onRejected || this._catch;
this._pending.push({resolve: onFulfilled, reject: onRejected});
return this;
};
Promise.prototype.catch = function (onRejected) {
// var onFulfilled = function (result) {
// return result;
// };
this._catch = onRejected || this._catch;
// this._pending.push({resolve: onFulfilled, reject: onRejected});
return this;
};
Promise.all = function (array) {
return new Promise(function () {
var self = this;
var counter = 0;
var finishResult = [];
function success(item, index) {
counter++;
finishResult[index] = item;
if (counter >= array.length) {
self.resolve(finishResult);
}
}
for(var i in array) {
var item = array[i];
if (item instanceof Promise) {
item.then(function (result) {
success(result,this);
}.bind(i), function (error) {
array.map(function (item) {
item.PromiseState = Promise.REJECTED
});
self._catch(error);
})
} else {
success(item, i);
}
}
});
};
Promise.race = function (array) {
return new Promise(function () {
var self = this;
var counter = 0;
var finishResult = [];
array.map(function (item) {
if (item instanceof Promise) {
item.then(function (result) {
array.map(function (item) {
item.PromiseState = Promise.REJECTED
});
self.resolve(result);
}, function (error) {
array.map(function (item) {
item.PromiseState = Promise.REJECTED
});
self._catch(error);
})
} else {
array.map(function (item) {
item.PromiseState = Promise.REJECTED
});
self.resolve(item);
}
})
});
};
Promise.resolve = function (value) {
return new Promise(function (resolve, reject) {
try {
resolve(value);
} catch (error) {
reject(error);
}
});
};
Promise.reject = function (error) {
return new Promise(function (resolve, reject) {
reject(error);
});
}
Discussing here.
Fiddle: here.
here is the absolute minimum of a promise architecture
function Promise(F) {
var gotoNext = false;
var stack = [];
var args = [];
var isFunction = function(f) {
return f && {}.toString.call(f) === '[object Function]';
};
var getArguments = function(self, _args) {
var SLICE = Array.prototype.slice;
_args = SLICE.call(_args);
_args.push(self);
return _args;
};
var callNext = function() {
var method = stack.shift();
gotoNext = false;
if (isFunction(method)) method.apply(null, args);
};
var resolve = [(function loop() {
if (stack.length) setTimeout(loop, 0);
if (gotoNext) callNext();
})];
this.return = function() {
gotoNext = true;
args = getArguments(this, arguments);
if(resolve.length) resolve.shift()();
return this;
};
this.then = function(fn) {
if (isFunction(fn)) stack.push(fn);
return this;
};
return this.then(F).return();
}
// --- below is a working implementation --- //
var bar = function(p) {
setTimeout(function() {
console.log("1");
p.return(2);
}, 1000);
};
var foo = function(num, p) {
setTimeout(function() {
console.log(num);
p.return(++num);
}, 1000);
};
new Promise(bar)
.then(foo)
.then(foo)
.then(foo);

Categories