Skip to content

Instantly share code, notes, and snippets.

@cbrunnkvist
Created January 20, 2016 06:41
Show Gist options
  • Save cbrunnkvist/844909c7cd7d86b54957 to your computer and use it in GitHub Desktop.
Save cbrunnkvist/844909c7cd7d86b54957 to your computer and use it in GitHub Desktop.
A verbose example of making JS/ES6 async code feel synchronous using generator/yield
'use strict';
const Promise = require('bluebird');
function someAsyncTask() {
return new Promise(function(resolve) {
let delay = Math.floor(Math.random() * 10000);
setTimeout(function () {
resolve(delay);
}, delay);
});
}
let main = Promise.coroutine(function* (sequenceName) {
console.log(sequenceName, 'starting 1st step');
let result1 = yield someAsyncTask();
console.log(sequenceName, 'starting 2nd step');
let result2 = yield someAsyncTask();
console.log(sequenceName, 'starting 3rd step');
let result3 = yield someAsyncTask();
console.log(sequenceName, 'done after', result1 + result2 + result3);
return result1 + result2 + result3;
});
main('first sequence');
main('second sequence');
console.log('all systems go!');
@mattccrampton
Copy link

This is the output I received...

first sequence starting 1st step
second sequence starting 1st step
all systems go!
first sequence starting 2nd step
first sequence starting 3rd step
second sequence starting 2nd step
first sequence done after 6216
second sequence starting 3rd step
second sequence done after 18070

I was expecting the first sequence stuff to all output before the second set did.

@vphantom
Copy link

It makes sense actually, because just like when we use regular callbacks, here both calls to main() return immediately at the first yield and the rest was postponed until later in the event loop. The sequence after "all systems go!" might even change randomly at each run...

@shakvaal
Copy link

@mattccrampton main returns promise.

@vkelman
Copy link

vkelman commented Jun 2, 2016

@cbrunnkvist, Off-topic question:
In your example above using "let" is equivalent of using "var", true? There are no block-scopes there. Is using "let" still a better style?

@jeff-kilbride
Copy link

@vkelman
For me, "let" is a better style. Besides block-scoping, all "var" declarations are hoisted, whereas "let" are not. Declaring variables with "let" more closely mimics the way variables are declared in other languages -- which is why I prefer it. In ES6 environments, I find myself using "const" and "let" almost exclusively.

@cbrunnkvist
Copy link
Author

cbrunnkvist commented Jan 5, 2017

Wow, I didn't realize there was any response to this gist. :-)

@vkelman yes there is almost never any reason to pick var nowadays. Each function block is a block scope but const/let takes away any ambiguities about the intent. Btw, I always strive after a "least privilege" code style, so I should have written const not let in all cases since I never change any references. I guess I sloppy-copy-pasted this example together.

@cbrunnkvist
Copy link
Author

cbrunnkvist commented Jan 5, 2017

@vphantom that's what I wanted to demonstrate: the two main instances kick off in sequence (the two initial "starting" lines of output) but get their job done asynchronously, in parallell (the jumbled "step" output). ⏲️ ⏲️

Yeah, I could have completed it with Promise.all([main1, main2]).then(()=>{console.log('all done')}) but that would have ruined whatever simplicity 😸 remains in the example IMO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment