3 ms·
Promises are just as easy to parallelize as callbacks, though. Easier, IMO, since you can pull their values more than once. Here's an example of some code I h
by phs2501 11y ago
Promises are just as easy to parallelize as callbacks, though. Easier, IMO, since you can pull their values more than once.
Here's an example of some code I had to write to query a somewhat nasty Web API:
async function apiCall(method, path, qs, body) { ... }
async function getPaginated(path, qs, extractionFn) {
let data = [];
let pagePromises = [
apiCall('GET', path, Object.assign({ }, qs, { page: 1 }))
];
let firstPage = await pagePromises[0];
for (let page = 2; page <= firstPage.pagination.page_count; ++page) {
pagePromises.push(
apiCall('GET', path, Object.assign({ }, qs, { page }))
);
}
let pages = await Promise.all(pagePromises);
for (let page of pages) {
let extracted = await extractionFn(page, qs);
data = data.concat(extracted);
}
return data;
}
Note that because of how promises work I didn't have to special case the storage of the first page (which needs to be fetched first to get the number of pages); I can just wait on it again and get the promise-cached answer. I don't know what async.js code to do that would look like but I doubt it'd be as succinct.
async functions magically return promises. You don't have to just await them, you can choose to do whatever you want with the resulting promises. And promises, being actual values and not just functions called at the right time, are much easier to compose.
Anyway, if you like the async.auto functionality, it wouldn't be very hard to write a Promise.auto that would let you do (compare to the async.auto example at https://github.com/caolan/async#auto https://github.com/caolan/async#auto):
let results = await Promise.auto({
get_data: async function(){
console.log('in get_data');
// async code to get some data
return [ 'data', 'converted to array' ];
},
make_folder: async function(){
console.log('in make_folder');
// async code to create a directory to store a file in
// this is run at the same time as getting the data
return 'folder';
},
write_file: ['get_data', 'make_folder', async function(results){
console.log('in write_file', JSON.stringify(results));
// once there is some data and the directory exists,
// write the data to a file in the directory
return 'filename';
}],
email_link: ['write_file', async function({write_file}){
console.log('in email_link', JSON.stringify(results));
// once the file is written let's email a link to it...
// results.write_file contains the filename returned by write_file.
return {'file':write_file, 'email':'user@example.com'};
}]
});
console.log('results = ', results);
...which is the same functionality except you don't have explicit callbacks running around everywhere. For that example, though, I'd rather just write a:
let [ data, folder ] = await Promise.all([get_data, make_folder]);
and do the rest as simple "sequential-ish" code.
I've written code with async.js, and it's nice. I vastly prefer Promises. The only thing I still use from async.js is the queueing stuff, and I wrap that up with Promises.
- phs2501 11y agoAs an example of non-trivial parallelism with Promises and async/await, here's a (slightly naive) implementation of Promise.auto I just whipped up. async function Promise_auto(clauses) { let keys = Object.keys(clauses); let results = { }; let clausePromises = { }; function runClause(key) { if (!clausePromises[key]) { clausePromises[key] = (async () => { let fn = clauses[key]; if (fn instanceof Array) { let deps = fn.slice(0, -1); fn = fn.slice(-1)[0]; await Promise.all(deps.map(runClause)); } console.log('start', key); results[key] = await fn(results); console.log('end', key); })(); } return clausePromises[key]; } await Promise.all(keys.map(runClause)); return results; } With the above (note clauses are intentionally out-of-order): (async () => { let results = await Promise_auto({ write_file: ['get_data', 'make_folder', async function(results){ console.log('in write_file', results); return 'filename'; }], email_link: ['write_file', async function(results){ console.log('in email_link', results); return {'file':results.write_file, 'email':'user@example.com'}; }], get_data: async function(){ console.log('in get_data'); return [ 'data', 'converted to array' ]; }, make_folder: async function(){ console.log('in make_folder'); return 'folder'; }, }); console.log('results = ', results); })(); => start get_data in get_data start make_folder in make_folder end get_data end make_folder start write_file in write_file { get_data: [ 'data', 'converted to array' ], make_folder: 'folder' } end write_file start email_link in email_link { get_data: [ 'data', 'converted to array' ], make_folder: 'folder', write_file: 'filename' } end email_link results = { get_data: [ 'data', 'converted to array' ], make_folder: 'folder', write_file: 'filename', email_link: { file: 'filename', email: 'user@example.com' } } Note that there was no topo-sorting or any other stuff to explicitly figure out an execution order, it just takes advantage of the fact that Promises can have their values pulled out multiple times to sequence things properly.