As I understand it ECMA6 generators are supposed to be able to yield to a function that returns a promise, eventually returning the resolved/rejected. Making the code read more like synchronous code, and avoiding callback hell.
I am using node.js v0.12.2 with --harmony and the following code.
var someAsyncThing = function() {
return new Promise(function(resolve, reject) {
resolve("I'm Resolved!");
});
};
someAsyncThing().then(function(res) {console.log(res);});
// Works as expected: logs I'm Resolved!
function* getPromise() {
var x = yield someAsyncThing();
console.log("x: " + x); // Fails x undefined
}
var y = getPromise();
console.log(y); // returns {}
console.log(y.next());
// Fails: logs { value: {}, done: false }
I've based the code off of the few examples I have been able to find online. What am I doing wrong?
As I understand it ECMA6 generators are supposed to be able to yield to a function that returns a promise, eventually returning the resolved/rejected.
No, that's not their purpose. ES6 generators are supposed to provide a simple way for writing iterators - each call to a generator function produces an iterator. An iterator is just a sequence of values - like an array, but consumed dynamically and produced lazily.
Now, generators can be abused for asynchronous control flow, by producing a sequence of promises that is consumed asynchronously and advancing the iterator with the results of each awaited promise. See here for an explanation without promises.
So what your code is missing is the consumer that actually waits for the promises and advances your generator. Usually you'd use a dedicated library (like co or task.js), or one of the helper functions that many promise libraries provide (Q, Bluebird, when, …), but for the purposes of this answer I'll show a simplified one:
function run(gf) {
let g = gf();
return Promise.resolve(function step(v) {
var res = g.next(v);
if (res.done) return res.value;
return res.value.then(step);
}());
}
Now with this function you can actually "execute" your getPromise generator:
run(getPromise).then(console.log, console.error);
tl;dr: The promise yielded by the generator has to move the generator forward.
If you look at first examples in http://davidwalsh.name/async-generators, you will notice that the async function actually moves the iterator forward:
function request(url) {
// this is where we're hiding the asynchronicity,
// away from the main code of our generator
// `it.next(..)` is the generator's iterator-resume
// call
makeAjaxCall( url, function(response){
it.next( response ); // <--- this is where the magic happens
} );
// Note: nothing returned here!
}
But since you are working with promises, we can improve on that, a little bit. y.next().value returns a promise, and you'd have to listen to that promise. So instead of writing console.log(y.next()), you'd write:
var promise = y.next().value;
promise.then(y.next.bind(y)); // or promise.then(function(v) { y.next(v); });
Babel demo
Of course this not very practical, because now you cannot access the next yielded value and you don't know when the generator will be done. However, you could write a recursive function which takes care of that. That's what runGenerator introduced later in this article takes care of.
Modern JavaScript has async/await syntax. For example:
function sleep(delay) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, delay);
} );
};
(async function () {
console.log('one');
await sleep(1000);
console.log('two');
await sleep(1000);
console.log('three');
})();
Prior to async/await syntax, you had the option of using Promises with function generators that yield promises. To do this, if you put the above code into https://babeljs.io and enable the babel-plugin-transform-async-to-generator it will produce the following code snippet.
function sleep(delay) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, delay);
} );
}
_asyncToGenerator(function *() {
console.log('one');
yield sleep(1000);
console.log('two');
yield sleep(1000);
console.log('three');
})();
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args)
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value)
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err)
}
_next(undefined)
})
}
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg)
var value = info.value
} catch (error) {
reject(error)
return
}
if (info.done) {
resolve(value)
} else {
Promise.resolve(value).then(_next, _throw)
}
}
N.B. this is a summary of a larger post here ES6 asynchronous generator result
Related
I am trying to figure out how to use Promise with generator. For that I am creating function helper to print async task but seems like I am doing it in the wrong way.
const asyncTask = () => new Promise((resolve, reject) => setTimeout(() => resolve('async task'), 1000))
function helper(func){
return func().next()
}
helper(function* main() {
try {
const a = yield asyncTask()
console.log(a)
} catch(e) {
console.error('error happened', e)
}
})
Getting no output.
I'm not 100% sure what you are trying to achieve. However, if you want to see the output of your promise, you have to await it and log it to the console. Your generator is producing your promise properly but you aren't doing anything with it. This alternative helper() function outputs the result:
async function helper(func){
let p = func().next().value;
let output = await p;
console.log(output);
}
It's generally easier to use asynchronous generators than to use synchronous generators mixed with other async aparatus.
An asynchronous generator works just like a normal generator except:
It's prefixed with async, like all async functions
next() returns a promise
Here's a simplified example:
let asyncGen = async function*() {
yield Promise.resolve('foo');
};
let iterator = asyncGen();
iterator.next().then(result => alert(result.value));
(Note, there's no difference between my Promise.resolve('foo') and your more convoluted promise with a timeout; both are resolved asynchronously - I just kept my example simple.)
You can read more about asynchronous generators in my three-part guide to generators.
You can look at the source code of the co.js to learn more about resolving generators as promises.
The c-promise2 package also has support of generators resolving:
CPromise.from(function*(){
const value1= yield CPromise.delay(3000, 3);
// Run promises in parallel using CPromise.all (shortcut syntax)
const [value2, value3]= yield [CPromise.delay(3000, 4), CPromise.delay(3000, 5)]
return value1 + value2 + value3;
}).then(function*(value){
console.log(`Done: ${value}`); // Done: 12
}, err=>{
console.log(`Failed: ${err}`);
})
Or just use ECMA async generators...
in one of youtube tutorial videos about promises I found following code:
let cleanRoom = function() {
return new Promise((resolve, reject) => {
resolve();
});
};
let removeGarbage = function() {
return new Promise((resolve, reject) => {
resolve();
});
};
let winIcecream = function() {
return new Promise((resolve, reject) => {
resolve();
});
};
cleanRoom().then(function(){
return removeGarbage();
}).then(function() {
return winIcecream();
}).then(function() {
console.log('finished');
})
Why the promises aren't chained like that the every .then word is after the previous promise? I mean, why for example .then is not immediately after removeGarbage() but it is after the cleanRoom().then(), how is it happening that the winIcecream() will run after resolving the removeGarbage promise?
Also do I need to type return declaring every promise like in the code above? If yes, why do I need to do so?
Your initial questions may be answered by rewriting things to variable assignments.
I'm using arrow function syntax to implicitly return the new expression here; if you're using regular functions, then yes, you do have to return the new chained promise if you want to run them in sequence.
const roomCleanedPromise = cleanRoom();
const roomCleanedAndGarbageTakenOutPromise = roomCleanedPromise.then(() => removeGarbage());
const roomCleanedAndGarbageTakenOutAndIcecreamWonPromise = roomCleanedAndGarbageTakenOutPromise.then(() => winIcecream());
const finishedPromise = roomCleanedAndGarbageTakenOutAndIcecreamWonPromise.then(() => console.log('finished'));
However things are easier written using the more modern async/await syntax - the YouTube tutorial you mention is a little outdated, perhaps.
async function cleanRoom() {
console.log('Cleaning room.');
// this could do other async things or just take a while
return {room: 'clean'}; // just to demonstrate a return value
}
async function removeGarbage() {
console.log('Removing garbage.');
// this could do other async things or just take a while
return {garbage: 'removed'};
}
// third function elided for brevity
async function doAllTheThings() {
const roomStatus = await cleanRoom();
const garbageStatus = await removeGarbage();
console.log('finished');
}
The purpose of using a fulfillment handler (the functions passed to then in your example) is to wait for the promise to be fulfilled and then, at that point, do something else.
The goal of that code (apparently) is to wait until the cleanRoom promise is fulfilled, then start the removeGarbage process, and then when that is fulfilled, start the winIcecream process. It's also worth noting that if cleanRoom's promise was rejected instead of being fulfilled, removeGarbage wouldn't happen at all, because it's in a fulfillment handler, not a rejection handler.
If you did this instead:
cleanRoom().then(function() { /*...*/ });
removeGarbage().then(function() { /*...*/ });
winIcecream().then(function() { /*...*/ });
...all three processes would be started immediately and run in parallel (to the extent whatever async process they're modelling can run in parallel with other JavaScript code). There'd be no coordination between them at all.
...how is it happening that the winIcecream() will run after resolving the removeGarbage promise...
then, catch, and finally create and return new promises. Those promises are fulfilled or rejected based on what happens to the promise they were called on and what happens in or is returned by their handler. So for example:
doThis()
.then(function() { return doThat(); })
.then(function() { console.log("done"); });
Let's call the promise from doThis() "Promise A". Calling then on it creates a new promise ("Promise B"), that will either be rejected (if Promise A is rejected) or will call its handler if Promise A is fulfilled. Promise B is resolved to whatever that handler returns. In the code above, suppose Promise A is fulfilled and doThat() returns a promise ("Promise C"). Now, Promise B is resolved to Promise C — whatever happens to Promise C is what will happen to Promise B. If Promise C is fulfilled, Promise B is fulfilled, and the second handler with the console.log is called.
The MDN article on using promises may be helpful.
Your suggestion / thinking I mean, why for example .then is not immediately after removeGarbage() but it is after the cleanRoom().then() is wrong.
Each promise ( .then ) is not execute immediately.
You can take a look at your example ( little edited by me )
const cleanRoom = () => new Promise((resolve, reject) => {
resolve();
});
const removeGarbage = () => new Promise((resolve, reject) => {
resolve();
});
const winIcecream = () => new Promise((resolve, reject) => {
resolve();
})
cleanRoom().then(function(){
console.log('second');
return removeGarbage();
}).then(function() {
return winIcecream();
}).then(function() {
console.log('finished');
})
console.log('first');
You should read more how the event loop works.
I have an array of promise objects that must be resolved in the same sequence in which they are listed in the array, i.e. we cannot attempt resolving an element till the previous one has been resolved (as method Promise.all([...]) does).
And if one element is rejected, I need the chain to reject at once, without attempting to resolve the following element.
How can I implement this, or is there an existing implementation for such sequence pattern?
function sequence(arr) {
return new Promise(function (resolve, reject) {
// try resolving all elements in 'arr',
// but strictly one after another;
});
}
EDIT
The initial answers suggest we can only sequence results of such array elements, not their execution, because it is predefined in such example.
But then how to generate an array of promises in such a way as to avoid early execution?
Here's a modified example:
function sequence(nextPromise) {
// while nextPromise() creates and returns another promise,
// continue resolving it;
}
I wouldn't want to make it into a separate question, because I believe it is part of the same problem.
SOLUTION
Some answers below and discussions that followed went a bit astray, but the eventual solution that did exactly what I was looking for was implemented within spex library, as method sequence. The method can iterate through a sequence of dynamic length, and create promises as required by the business logic of your application.
Later on I turned it into a shared library for everyone to use.
Here are some simple examples for how you sequence through an array executing each async operation serially (one after the other).
Let's suppose you have an array of items:
var arr = [...];
And, you want to carry out a specific async operation on each item in the array, one at a time serially such that the next operation does not start until the previous one has finished.
And, let's suppose you have a promise returning function for processing one of the items in the array fn(item):
Manual Iteration
function processItem(item) {
// do async operation and process the result
// return a promise
}
Then, you can do something like this:
function processArray(array, fn) {
var index = 0;
function next() {
if (index < array.length) {
fn(array[index++]).then(next);
}
}
return next();
}
processArray(arr, processItem);
Manual Iteration Returning Promise
If you wanted a promise returned from processArray() so you'd know when it was done, you could add this to it:
function processArray(array, fn) {
var index = 0;
function next() {
if (index < array.length) {
return fn(array[index++]).then(function(value) {
// apply some logic to value
// you have three options here:
// 1) Call next() to continue processing the result of the array
// 2) throw err to stop processing and result in a rejected promise being returned
// 3) return value to stop processing and result in a resolved promise being returned
return next();
});
}
} else {
// return whatever you want to return when all processing is done
// this returne value will be the ersolved value of the returned promise.
return "all done";
}
return next();
}
processArray(arr, processItem).then(function(result) {
// all done here
console.log(result);
}, function(err) {
// rejection happened
console.log(err);
});
Note: this will stop the chain on the first rejection and pass that reason back to the processArray returned promise.
Iteration with .reduce()
If you wanted to do more of the work with promises, you could chain all the promises:
function processArray(array, fn) {
return array.reduce(function(p, item) {
return p.then(function() {
return fn(item);
});
}, Promise.resolve());
}
processArray(arr, processItem).then(function(result) {
// all done here
}, function(reason) {
// rejection happened
});
Note: this will stop the chain on the first rejection and pass that reason back to the promise returned from processArray().
For a success scenario, the promise returned from processArray() will be resolved with the last resolved value of your fn callback. If you wanted to accumulate a list of results and resolve with that, you could collect the results in a closure array from fn and continue to return that array each time so the final resolve would be an array of results.
Iteration with .reduce() that Resolves With Array
And, since it now seems apparent that you want the final promise result to be an array of data (in order), here's a revision of the previous solution that produces that:
function processArray(array, fn) {
var results = [];
return array.reduce(function(p, item) {
return p.then(function() {
return fn(item).then(function(data) {
results.push(data);
return results;
});
});
}, Promise.resolve());
}
processArray(arr, processItem).then(function(result) {
// all done here
// array of data here in result
}, function(reason) {
// rejection happened
});
Working demo: http://jsfiddle.net/jfriend00/h3zaw8u8/
And a working demo that shows a rejection: http://jsfiddle.net/jfriend00/p0ffbpoc/
Iteration with .reduce() that Resolves With Array with delay
And, if you want to insert a small delay between operations:
function delay(t, v) {
return new Promise(function(resolve) {
setTimeout(resolve.bind(null, v), t);
});
}
function processArrayWithDelay(array, t, fn) {
var results = [];
return array.reduce(function(p, item) {
return p.then(function() {
return fn(item).then(function(data) {
results.push(data);
return delay(t, results);
});
});
}, Promise.resolve());
}
processArray(arr, 200, processItem).then(function(result) {
// all done here
// array of data here in result
}, function(reason) {
// rejection happened
});
Iteration with Bluebird Promise Library
The Bluebird promise library has a lot of concurrency controlling features built right in. For example, to sequence iteration through an array, you can use Promise.mapSeries().
Promise.mapSeries(arr, function(item) {
// process each individual item here, return a promise
return processItem(item);
}).then(function(results) {
// process final results here
}).catch(function(err) {
// process array here
});
Or to insert a delay between iterations:
Promise.mapSeries(arr, function(item) {
// process each individual item here, return a promise
return processItem(item).delay(100);
}).then(function(results) {
// process final results here
}).catch(function(err) {
// process array here
});
Using ES7 async/await
If you're coding in an environment that supports async/await, you can also just use a regular for loop and then await a promise in the loop and it will cause the for loop to pause until a promise is resolved before proceeding. This will effectively sequence your async operations so the next one doesn't start until the previous one is done.
async function processArray(array, fn) {
let results = [];
for (let i = 0; i < array.length; i++) {
let r = await fn(array[i]);
results.push(r);
}
return results; // will be resolved value of promise
}
// sample usage
processArray(arr, processItem).then(function(result) {
// all done here
// array of data here in result
}, function(reason) {
// rejection happened
});
FYI, I think my processArray() function here is very similar to Promise.map() in the Bluebird promise library which takes an array and a promise producing function and returns a promise that resolves with an array of resolved results.
#vitaly-t - Here some some more detailed comments on your approach. You are welcome to whatever code seems best to you. When I first started using promises, I tended to use promises only for the simplest things they did and write a lot of the logic myself when a more advanced use of promises could do much more of it for me. You use only what you are fully comfortable with and beyond that, you'd rather see your own code that you intimately know. That's probably human nature.
I will suggest that as I understood more and more of what promises can do for me, I now like to write code that uses more of the advanced features of promises and it seems perfectly natural to me and I feel like I'm building on well tested infrastructure that has lots of useful features. I'd only ask that you keep your mind open as you learn more and more to potentially go that direction. It's my opinion that it's a useful and productive direction to migrate as your understanding improves.
Here are some specific points of feedback on your approach:
You create promises in seven places
As a contrast in styles, my code has only two places where I explicitly create a new promise - once in the factory function and once to initialize the .reduce() loop. Everywhere else, I'm just building on the promises already created by chaining to them or returning values within them or just returning them directly. Your code has seven unique places where you're creating a promise. Now, good coding isn't a contest to see how few places you can create a promise, but that might point out the difference in leverage the promises that are already created versus testing conditions and creating new promises.
Throw-safety is a very useful feature
Promises are throw-safe. That means that an exception thrown within a promise handler will automatically reject that promise. If you just want the exception to become a rejection, then this is a very useful feature to take advantage of. In fact, you will find that just throwing yourself is a useful way to reject from within a handler without creating yet another promise.
Lots of Promise.resolve() or Promise.reject() is probably an opportunity for simplification
If you see code with lots of Promise.resolve() or Promise.reject() statements, then there are probably opportunities to leverage the existing promises better rather than creating all these new promises.
Cast to a Promise
If you don't know if something returned a promise, then you can cast it to a promise. The promise library will then do it's own checks whether it is a promise or not and even whether it's the kind of promise that matches the promise library you're using and, if not, wrap it into one. This can save rewriting a lot of this logic yourself.
Contract to Return a Promise
In many cases these days, it's completely viable to have a contract for a function that may do something asynchronous to return a promise. If the function just wants to do something synchronous, then it can just return a resolved promise. You seem to feel like this is onerous, but it's definitely the way the wind is blowing and I already write lots of code that requires that and it feels very natural once you get familiar with promises. It abstracts away whether the operation is sync or async and the caller doesn't have to know or do anything special either way. This is a nice use of promises.
The factory function can be written to create one promise only
The factory function can be written to create one promise only and then resolve or reject it. This style also makes it throw safe so any exception occuring in the factory function automatically becomes a reject. It also makes the contract to always return a promise automatic.
While I realize this factory function is a placeholder function (it doesn't even do anything async), hopefully you can see the style to consider it:
function factory(idx) {
// create the promise this way gives you automatic throw-safety
return new Promise(function(resolve, reject) {
switch (idx) {
case 0:
resolve("one");
break;
case 1:
resolve("two");
break;
case 2:
resolve("three");
break;
default:
resolve(null);
break;
}
});
}
If any of these operations were async, then they could just return their own promises which would automatically chain to the one central promise like this:
function factory(idx) {
// create the promise this way gives you automatic throw-safety
return new Promise(function(resolve, reject) {
switch (idx) {
case 0:
resolve($.ajax(...));
case 1:
resole($.ajax(...));
case 2:
resolve("two");
break;
default:
resolve(null);
break;
}
});
}
Using a reject handler to just return promise.reject(reason) is not needed
When you have this body of code:
return obj.then(function (data) {
result.push(data);
return loop(++idx, result);
}, function (reason) {
return promise.reject(reason);
});
The reject handler is not adding any value. You can instead just do this:
return obj.then(function (data) {
result.push(data);
return loop(++idx, result);
});
You are already returning the result of obj.then(). If either obj rejects or if anything chained to obj or returned from then .then() handler rejects, then obj will reject. So you don't need to create a new promise with the reject. The simpler code without the reject handler does the same thing with less code.
Here's a version in the general architecture of your code that tries to incorporate most of these ideas:
function factory(idx) {
// create the promise this way gives you automatic throw-safety
return new Promise(function(resolve, reject) {
switch (idx) {
case 0:
resolve("zero");
break;
case 1:
resolve("one");
break;
case 2:
resolve("two");
break;
default:
// stop further processing
resolve(null);
break;
}
});
}
// Sequentially resolves dynamic promises returned by a factory;
function sequence(factory) {
function loop(idx, result) {
return Promise.resolve(factory(idx)).then(function(val) {
// if resolved value is not null, then store result and keep going
if (val !== null) {
result.push(val);
// return promise from next call to loop() which will automatically chain
return loop(++idx, result);
} else {
// if we got null, then we're done so return results
return result;
}
});
}
return loop(0, []);
}
sequence(factory).then(function(results) {
log("results: ", results);
}, function(reason) {
log("rejected: ", reason);
});
Working demo: http://jsfiddle.net/jfriend00/h3zaw8u8/
Some comments about this implementation:
Promise.resolve(factory(idx)) essentially casts the result of factory(idx) to a promise. If it was just a value, then it becomes a resolved promise with that return value as the resolve value. If it was already a promise, then it just chains to that promise. So, it replaces all your type checking code on the return value of the factory() function.
The factory function signals that it is done by returning either null or a promise whose resolved value ends up being null. The above cast maps those two conditions to the same resulting code.
The factory function catches exceptions automatically and turns them into rejects which are then handled automatically by the sequence() function. This is one significant advantage of letting promises do a lot of your error handling if you just want to abort processing and feed the error back on the first exception or rejection.
The factory function in this implementation can return either a promise or a static value (for a synchronous operation) and it will work just fine (per your design request).
I've tested it with a thrown exception in the promise callback in the factory function and it does indeed just reject and propagate that exception back to reject the sequence promise with the exception as the reason.
This uses a similar method as you (on purpose, trying to stay with your general architecture) for chaining multiple calls to loop().
Promises represent values of operations and not the operations themselves. The operations are already started so you can't make them wait for one another.
Instead, you can synchronize functions that return promises invoking them in order (through a loop with promise chaining for instance), or using the .each method in bluebird.
You can't simply run X async operations and then want them to be resolved in an order.
The correct way to do something like this is to run the new async operation only after the one before was resolved:
doSomethingAsync().then(function(){
doSomethingAsync2().then(function(){
doSomethingAsync3();
.......
});
});
Edit Seems like you want to wait for all promises and then invoke their callbacks in a specific order. Something like this:
var callbackArr = [];
var promiseArr = [];
promiseArr.push(doSomethingAsync());
callbackArr.push(doSomethingAsyncCallback);
promiseArr.push(doSomethingAsync1());
callbackArr.push(doSomethingAsync1Callback);
.........
promiseArr.push(doSomethingAsyncN());
callbackArr.push(doSomethingAsyncNCallback);
and then:
$.when(promiseArr).done(function(promise){
while(callbackArr.length > 0)
{
callbackArr.pop()(promise);
}
});
The problems that can occur with this is when one or more promises fail.
Although quite dense, here's another solution that will iterate a promise-returning function over an array of values and resolve with an array of results:
function processArray(arr, fn) {
return arr.reduce(
(p, v) => p.then((a) => fn(v).then(r => a.concat([r]))),
Promise.resolve([])
);
}
Usage:
const numbers = [0, 4, 20, 100];
const multiplyBy3 = (x) => new Promise(res => res(x * 3));
// Prints [ 0, 12, 60, 300 ]
processArray(numbers, multiplyBy3).then(console.log);
Note that, because we're reducing from one promise to the next, each item is processed in series.
It's functionally equivalent to the "Iteration with .reduce() that Resolves With Array" solution from #jfriend00 but a bit neater.
I suppose two approaches for handling this question:
Create multiple promises and use the allWithAsync function as follow:
let allPromiseAsync = (...PromisesList) => {
return new Promise(async resolve => {
let output = []
for (let promise of PromisesList) {
output.push(await promise.then(async resolvedData => await resolvedData))
if (output.length === PromisesList.length) resolve(output)
}
}) }
const prm1= Promise.resolve('first');
const prm2= new Promise((resolve, reject) => setTimeout(resolve, 2000, 'second'));
const prm3= Promise.resolve('third');
allPromiseAsync(prm1, prm2, prm3)
.then(resolvedData => {
console.log(resolvedData) // ['first', 'second', 'third']
});
Use the Promise.all function instead:
(async () => {
const promise1 = new Promise(resolve => {
setTimeout(() => { console.log('first');console.log(new Date());resolve() }, 1000)
})
const promise2 = new Promise(resolve => {
setTimeout(() => {console.log('second');console.log(new Date()); resolve() }, 3000)
})
const promise3 = new Promise(resolve => {
setTimeout(() => { console.log('third');console.log(new Date()); resolve() }, 7000)
})
const promises = [promise1, promise2, promise3]
await Promise.all(promises)
console.log('This line is shown after 7000ms')
})()
In my opinion, you should be using a for loop(yes the only time I would recommend a for loop). The reason is that when you are using a for loop it allows you to await on each of the iterations of your loop where using reduce, map or forEach with run all your promise iterations concurrently. Which by the sounds of it is not what you want, you want each promise to wait until the previous promise has resolved. So to do this you would do something like the following.
const ids = [0, 1, 2]
const accounts = ids.map(id => getId(id))
const accountData = async() => {
for await (const account of accounts) {
// account will equal the current iteration of the loop
// and each promise are now waiting on the previous promise to resolve!
}
}
// then invoke your function where ever needed
accountData()
And obviously, if you wanted to get really extreme you could do something like this:
const accountData = async(accounts) => {
for await (const account of accounts) {
// do something
}
}
accountData([0, 1, 2].map(id => getId(id)))
This is so much more readable than any of the other examples, it is much less code, reduced the number of lines needed for this functionality, follows a more functional programming way of doing things and is using ES7 to its full potential!!!!
Also depending on your set up or when you are reading this you may need to add the plugin-proposal-async-generator-functions polyfill or you may see the following error
#babel/plugin-proposal-async-generator-functions (https://git.io/vb4yp) to the 'plugins' section of your Babel config to enable transformation.
This question already has answers here:
Why does the Promise constructor need an executor?
(2 answers)
Closed 6 years ago.
I wish to return a Promise which is self-resolved at a later time, but it seems that my syntax is invalid, and I'm curious what a better implementation would be. Why is an executor required for the Promise constructor?
promise = new Promise() is invalid because I need to supply a function
function getDetails (someHash) {
var url = "http://somewebsite.com/" + someHash,
promise = new Promise();
makeAjaxRequest(url, function (response) {
promise.resolve(response.data);
});
setTimeout(function () {
promise.reject("timeout");
}, 500);
return promise;
}
function makeAjaxRequest (url, callback) {
var someResponse = {data: "some data"};
setTimeout(function () {
callback(someResponse);
}, 200);
}
Is there a better way to implement this functionality?
Note: If you want to convert a callback API to promises see this question.
Let's start with something that should be said from the get go. An executor is not required in order to design promises. It is entirely possible to design a promises implementation that does something like:
let {promise, resolve, reject} = Promise.get();
If you promise not to tell anyone, I'll even let you in on a little secret - this API even exists from a previous iteration of the spec and in fact even still works in Chrome:
let {promise, resolve, reject} = Promise.defer();
However, it is being removed.
So, why do I need to pass an executor?
I just answered your other question about why an executor is an interesting design. It's throw-safe and it lets you take care of interesting things.
Can I still get the resolve, reject functions? I need those
In my experience, most of the times I needed resolve/reject I didn't actually need them. That said - I did in fact actually need them a couple of times.
The specifiers recognized this and for this reason the executor function is always run synchronously. You can get the .defer interface it just isn't the default:
function defer() {
let resolve, reject, promise = new Promise((res, rej) => {
[resolve, reject] = [res, rej];
});
return {resolve, reject, promise};
}
Again, this is typically not something you want to do but it is entirely possible that you have a justifiable use case which is why it's not the default but completely possible.
Your actual code
I would start with implementing things like timeout and a request as primitives and then compose functions and chain promises:
function delay(ms) {
return new Promise(r => setTimeout(r, ms));
}
function timeout(promise, ms) {
return Promise.race([
promise,
delay(ms).then(x => { throw new Error("timeout"); })
]);
}
function ajax(url) { // note browsers do this natively with `fetch` today
return new Promise((resolve, reject) => { // handle errors!
makeAjaxRequest(url, (result) => {
// if(result.isFailure) reject(...);
if(result.status >= 400) reject(new Error(result.status));
else resolve(result.data);
});
});
}
Now when we promisified the lowest level API surface we can write the above code quite declaratively:
function getDetails (someHash) {
var ajax = makeAjaxRequest("http://somewebsite.com/" + someHash);
return timeout(ajax, 500);
}
You need to pass a function to the Promise constructor (more info), which will get called to provide the resolve and reject functions:
function getDetails (someHash) {
var url = "http://somewebsite.com/" + someHash;
return new Promise(function(resolve, reject) {
makeAjaxRequest(url, function(err, response) {
if (err)
reject(err);
else
resolve(response.data);
});
setTimeout(function () {
reject("timeout");
}, 500);
});
}
function makeAjaxRequest (url, callback) {
var someResponse = {data: "some data"};
setTimeout(function () {
callback(null, someResponse);
}, 200);
}
I've also taken the liberty to make makeAjaxRequest use the standard convention of passing errors as first argument (because I assume that at some point you want to replace the setTimeout() with an actual AJAX request).
I have an array of functions that I need to execute.
Those functions all return a promise.
I want to run all the functions in series, but the next function can only be started if the promise of the previous function is done.
I thought this would be easy with the async or bluebird library, but I can't find a simple solution to this.
Here is something I made (untested), but I was looking for a standard library solution because this probably already exists?
function runFuncs(funcs) {
function funcRunner(funcs, resolve, reject, pos=0) {
funcs[pos]().then(function() {
pos++;
if (pos < length(funcs)) {
funcRunner(funcs, pos);
} else {
resolve();
}
}).catch(function(err) {
reject(err);
});
}
return new Promise(function(resolve, reject) {
funcRunner(funcs, resolve, reject);
});
}
If each of the functions returns a promise itself, you can just chain them together. Something like this should work:
function runFuncs(funcs) {
return funcs.reduce(function (p, funcToRun) {
return p.then(function () {
return funcToRun();
});
}, Promise.resolve(null));
}
Basically, you just keep chaining the promises together. By the nature of the chain, if you return a promise from the then handler, that becomes the resulting promise. So the reduce call goes through each promise in the array, doing a then on it to process the next one in the array.
I know you've marked an answer already, but I can't comment yet and had to ask: Your example doesn't seem to pass results from one function to another, do you really need them to run in series?
If series is a hard requirement, Bluebird's .mapSeries() method will do exactly what you're looking for, and the code is incredibly neat. Otherwise you can swap in .map() and leverage concurrency to get things done in parallel.
Promise.mapSeries(fnArray, (fn) =>
{
return fn(...);
})
.then((results) =>
{
// do something with the results.
});
Or in parallel with .map():
Promise.map(fnArray, (fn) =>
{
return fn(...);
},
{ concurrency: 5 })
.then((results) =>
{
// do something with the results.
});
Using no additional libraries, just plain old JS code, I was able to come up with a simple solution.
'use strict';
// Create an array of functions that return a promise
const promises = [];
let i = 0;
while (i++ < 10) {
const v = i;
promises.push(() => {
return new Promise( (resolve, reject) => {
setTimeout(() => {
resolve(v);
}, Math.floor(Math.random() * 1000));
});
});
}
function nextPromise(idx) {
if (idx >= promises.length) return;
promises[idx]().then((v) => {
console.log("Resolved", v);
nextPromise(++idx);
});
}
nextPromise(0);
In this example, I created an array of 10 functions, each of which returns a promise which resolves a monotonically increasing number after random number of milliseconds.
To execute them all sequentially, I just use some recursion. It's kind of an ugly bit of hackey looking code, but it works.
If "funcs" is array of promises, you can to use Promise.all() - Promise docs
import {Promise} from 'es6-promise';
const runFuncs = funcs => {
return Promise.all(funcs);
};
export default runFuncs;