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
});
Related
I read that async functions marked by the async keyword implicitly return a promise:
async function getVal(){
return await doSomethingAync();
}
var ret = getVal();
console.log(ret);
but that is not coherent...assuming doSomethingAsync() returns a promise, and the await keyword will return the value from the promise, not the promise itsef, then my getVal function should return that value, not an implicit promise.
So what exactly is the case? Do functions marked by the async keyword implicitly return promises or do we control what they return?
Perhaps if we don't explicitly return something, then they implicitly return a promise...?
To be more clear, there is a difference between the above and
function doSomethingAync(charlie) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(charlie || 'yikes');
}, 100);
})
}
async function getVal(){
var val = await doSomethingAync(); // val is not a promise
console.log(val); // logs 'yikes' or whatever
return val; // but this returns a promise
}
var ret = getVal();
console.log(ret); //logs a promise
In my synopsis the behavior is indeed inconsistent with traditional return statements. It appears that when you explicitly return a non-promise value from an async function, it will force wrap it in a promise.
I don't have a big problem with it, but it does defy normal JS.
The return value will always be a promise. If you don't explicitly return a promise, the value you return will automatically be wrapped in a promise.
async function increment(num) {
return num + 1;
}
// Even though you returned a number, the value is
// automatically wrapped in a promise, so we call
// `then` on it to access the returned value.
//
// Logs: 4
increment(3).then(num => console.log(num));
Same thing even if there's no return! (Promise { undefined } is returned)
async function increment(num) {}
Same thing even if there's an await.
function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback());
}, 1000);
});
}
async function incrementTwice(num) {
const numPlus1 = await defer(() => num + 1);
return numPlus1 + 1;
}
// Logs: 5
incrementTwice(3).then(num => console.log(num));
Promises auto-unwrap, so if you do return a promise for a value from within an async function, you will receive a promise for the value (not a promise for a promise for the value).
function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback());
}, 1000);
});
}
async function increment(num) {
// It doesn't matter whether you put an `await` here.
return defer(() => num + 1);
}
// Logs: 4
increment(3).then(num => console.log(num));
In my synopsis the behavior is indeed inconsistent with traditional
return statements. It appears that when you explicitly return a
non-promise value from an async function, it will force wrap it in a
promise. I don't have a big problem with it, but it does defy normal
JS.
ES6 has functions which don't return exactly the same value as the return. These functions are called generators.
function* foo() {
return 'test';
}
// Logs an object.
console.log(foo());
// Logs 'test'.
console.log(foo().next().value);
Yes, an async function will always return a promise.
According to the tc39 spec, an async function desugars to a generator which yields Promises.
Specifically:
async function <name>?<argumentlist><body>
Desugars to:
function <name>?<argumentlist>{ return spawn(function*() <body>, this); }
Where spawn "is a call to the following algorithm":
function spawn(genF, self) {
return new Promise(function(resolve, reject) {
var gen = genF.call(self);
function step(nextF) {
var next;
try {
next = nextF();
} catch(e) {
// finished with failure, reject the promise
reject(e);
return;
}
if(next.done) {
// finished with success, resolve the promise
resolve(next.value);
return;
}
// not finished, chain off the yielded promise and `step` again
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
Your question is: If I create an async function should it return a promise or not? Answer: just do whatever you want and Javascript will fix it for you.
Suppose doSomethingAsync is a function that returns a promise. Then
async function getVal(){
return await doSomethingAsync();
}
is exactly the same as
async function getVal(){
return doSomethingAsync();
}
You probably are thinking "WTF, how can these be the same?" and you are right. The async will magically wrap a value with a Promise if necessary.
Even stranger, the doSomethingAsync can be written to sometimes return a promise and sometimes NOT return a promise. Still both functions are exactly the same, because the await is also magic. It will unwrap a Promise if necessary but it will have no effect on things that are not Promises.
Just add await before your function when you call it :
var ret = await getVal();
console.log(ret);
async doesn't return the promise, the await keyword awaits the resolution of the promise. async is an enhanced generator function and await works a bit like yield
I think the syntax (I am not 100% sure) is
async function* getVal() {...}
ES2016 generator functions work a bit like this. I have made a database handler based in top of tedious which you program like this
db.exec(function*(connection) {
if (params.passwd1 === '') {
let sql = 'UPDATE People SET UserName = #username WHERE ClinicianID = #clinicianid';
let request = connection.request(sql);
request.addParameter('username',db.TYPES.VarChar,params.username);
request.addParameter('clinicianid',db.TYPES.Int,uid);
yield connection.execSql();
} else {
if (!/^\S{4,}$/.test(params.passwd1)) {
response.end(JSON.stringify(
{status: false, passwd1: false,passwd2: true}
));
return;
}
let request = connection.request('SetPassword');
request.addParameter('userID',db.TYPES.Int,uid);
request.addParameter('username',db.TYPES.NVarChar,params.username);
request.addParameter('password',db.TYPES.VarChar,params.passwd1);
yield connection.callProcedure();
}
response.end(JSON.stringify({status: true}));
}).catch(err => {
logger('database',err.message);
response.end(JSON.stringify({status: false,passwd1: false,passwd2: false}));
});
Notice how I just program it like normal synchronous particularly at
yield connection.execSql and at yield connection.callProcedure
The db.exec function is a fairly typical Promise based generator
exec(generator) {
var self = this;
var it;
return new Promise((accept,reject) => {
var myConnection;
var onResult = lastPromiseResult => {
var obj = it.next(lastPromiseResult);
if (!obj.done) {
obj.value.then(onResult,reject);
} else {
if (myConnection) {
myConnection.release();
}
accept(obj.value);
}
};
self._connection().then(connection => {
myConnection = connection;
it = generator(connection); //This passes it into the generator
onResult(); //starts the generator
}).catch(error => {
reject(error);
});
});
}
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);
}, []);
}
I am experimenting some strange behaviours with jQuery promises on reject.
I have an array of promises, and I need to work with them when they all have been resolved/rejected.
To do so, I am using this:
var array_res = [];
array_res.push(promiseResolve('a'));
array_res.push(promiseReject('b'));
$.when.apply(null,array_res).always( function ( ) {
console.log(arguments);
//Work to do
} );
function promiseResolve (c) {
var promise = $.Deferred();
promise.resolve({a:c});
return promise;
}
function promiseReject (c) {
var promise = $.Deferred();
promise.reject({b:c});
return promise;
}
The issue is:
If I resolve both promises, everything works fine.
If I reject one of the promises, then the arguments come incomplete.
If I reject both of them, then the arguments come incomplete.
Here are 3 fiddles where you can check the behaviour:
https://jsfiddle.net/daepqzv1/1/
https://jsfiddle.net/daepqzv1/2/
https://jsfiddle.net/daepqzv1/3/
What I need is a way to get the arguments for both, rejected and resolved.
This is normal behavior for $.when(). If any promises you pass to $.when() reject, then $.when() will reject with the first reject reason that it finds. This is how it is coded.
This is similar to the way the ES6 Promise.all() works.
If you want all results, even if some promises reject, then you can use something like $.settle() or $.settleVal() that are defined in this code:
(function() {
function isPromise(p) {
return p && (typeof p === "object" || typeof p === "function") && typeof p.then === "function";
}
function wrapInPromise(p) {
if (!isPromise(p)) {
p = $.Deferred().resolve(p);
}
return p;
}
function PromiseInspection(fulfilled, val) {
return {
isFulfilled: function() {
return fulfilled;
}, isRejected: function() {
return !fulfilled;
}, isPending: function() {
// PromiseInspection objects created here are never pending
return false;
}, value: function() {
if (!fulfilled) {
throw new Error("Can't call .value() on a promise that is not fulfilled");
}
return val;
}, reason: function() {
if (fulfilled) {
throw new Error("Can't call .reason() on a promise that is fulfilled");
}
return val;
}
};
}
// pass either multiple promises as separate arguments or an array of promises
$.settle = function(p1) {
var args;
if (Array.isArray(p1)) {
args = p1;
} else {
args = Array.prototype.slice.call(arguments);
}
return $.when.apply($, args.map(function(p) {
// make sure p is a promise (it could be just a value)
p = wrapInPromise(p);
// Now we know for sure that p is a promise
// Make sure that the returned promise here is always resolved with a PromiseInspection object, never rejected
return p.then(function(val) {
return new PromiseInspection(true, val);
}, function(reason) {
// convert rejected promise into resolved promise by returning a resolved promised
// One could just return the promiseInspection object directly if jQuery was
// Promise spec compliant, but jQuery 1.x and 2.x are not so we have to take this extra step
return wrapInPromise(new PromiseInspection(false, reason));
});
})).then(function() {
// return an array of results which is just more convenient to work with
// than the separate arguments that $.when() would normally return
return Array.prototype.slice.call(arguments);
});
}
// simpler version that just converts any failed promises
// to a resolved value of what is passed in, so the caller can just skip
// any of those values in the returned values array
// Typically, the caller would pass in null or 0 or an empty object
$.settleVal = function(errorVal, p1) {
var args;
if (Array.isArray(p1)) {
args = p1;
} else {
args = Array.prototype.slice.call(arguments, 1);
}
return $.when.apply($, args.map(function(p) {
p = wrapInPromise(p);
return p.then(null, function(err) {
return wrapInPromise(errorVal);
});
}));
}
})();
$.settle() always resolves and it resolves with an array of PromiseInspection objects that you can then iterate over to see which promises resolved and which rejected and what the value or reason was.
$.settleVal() is a little simpler to iterate, but a little less generic because it doesn't give you the reject reason. It always resolves with an array where a reject will have a default value that you pass to it in the array in place of the resolved value.
FYI, both $.settle() and $.settleVal() can both be passed an array of promises like $.settle(arrayOfPromises) or multiple promise arguments as $.settle(p1, p2, p3) (as $.when() works). This saves having to use .apply() when you have an array of promises.
The following code does not really do what I want.
function doIt () {
return new Promise (function (resolve, reject) {
var promises = [];
db.transaction(function(tx1){
tx1.executeSql(function(tx2, rs) {
for (var i = i; i < N; i++) {
promises.push(db.transaction(function(tx3){
...
}));
}
});
});
Promise.all(promises).then(resolve);
});
}
Now it does not work, because Promise.all() gets executed, before all promises are in the array, at least I think that's correct.
Is there a elegant way to guarantee that all these promises are finished, before doIt ends?
You can just move where the Promise.all() is located so that it's right AFTER the for loop has finished populating the array:
function doIt () {
return new Promise (function (resolve, reject) {
var promises = [];
db.transaction(function(tx1){
tx1.executeSql(function(tx2, rs) {
for (var i = i; i < N; i++) {
promises.push(db.transaction(function(tx3){
...
}));
}
Promise.all(promises).then(resolve);
});
});
});
}
FYI, mixing promises and callbacks can be confusing and makes consistent error handling particularly difficult. Does tx1.executeSql() already return a promise? If so, you can do something cleaner using only promises that are already created by your database functions like this:
function doIt() {
return db.transaction.then(function(tx1) {
return tx1.executeSql().then(function(tx2, rs) {
var promises = [];
for (var i = i; i < N; i++) {
promises.push(db.transaction().then(function(tx3) {
...
}));
}
return Promise.all(promises).then(resolve);
});
});
}
This returns promises from .then() handlers to auto-chain promises together.
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);