Jordan Kasper | @jakerella
const fs = require('fs');
fs.readFile('data-1.json', function(err, one) {
if (err) {
// Handle the error somehow
}
// Do something with the data...
});
const fs = require('fs');
fs.readFile('data-1.json', function(err, one) {
if (err) {
// Handle the error somehow
}
fs.readFile('data-' + one.nextIndex + '.json', function(err, more) {
// ...
});
});
const fs = require('fs');
fs.readFile('data-1.json', function(err, one) {
if (err) {
// Handle the error somehow
}
// Do something with the data...
fs.readFile('data-' + one.nextIndex + '.json', function(err, more) {
if (err) {
// Handle the error somehow
}
if (more.nextIndex) {
fs.readFile('data-' + more.nextIndex + '.json', function(err, extra) {
// ...
});
}
});
});
const one = JSON.parse(fs.readFileSync('data-1.json').toString());
// Do something with the data...
const more = JSON.parse(fs.readFileSync('data-' + one.nextIndex + '.json').toString());
if (more.nextIndex) {
const extra = fs.readFileSync('data-' + extra.nextIndex + '.json');
}
Not quite.
Promises are a mechanism for managing callbacks.
fetch('/api/users', { /* options */ })
.then(function(response) {
console.log(response.status);
})
.catch(function(err) {
// handle any error properly!
console.error('API error:', err);
});
fetch('/api/users', { /* options */ })
.then(function(response) {
console.log(response.status);
response.json()
.then(function(userData) {
// now we can use the data...
console.log(userData);
});
})
fetch('/api/users', { /* options */ })
.then(function(response) {
console.log(response.status);
return response.json();
})
.then(function(userData) {
// now we can use the data...
console.log(userData);
})
fetch('/api/users', { /* options */ })
.then(function(response) {
console.log(response.status);
return response.json();
})
.then(function(userData) {
// now we can use the data...
console.log(userData);
})
.catch(function(err) {
// handle any error properly!
console.error('API error:', err);
});
function apiCall(id, callback) {
ajaxCall({
url: '/api/user/' + id,
success: function(data) {
callback(null, data);
},
error: function(err) {
callback(err);
}
});
}
function apiCall(id) {
const thePromise = new Promise(function(resolve, reject) {
ajaxCall({
url: '/api/user/' + id,
success: function(data) {
resolve(data);
},
error: function(err) {
reject(err);
}
});
});
return thePromise;
}
apiCall(42, function(err, data) {
if (err) {
// Handle the error
}
console.log(data);
});
apiCall(42)
.then(function(data) {
console.log(data);
})
.catch(function(err) {
// Handle the error
})
...but it doesn't really get us where we want to be.
A mechanism for pausing (and resuming)
function execution.
setTimeout(function() {
console.log('100 ms timeout!');
}, 100);
// Let's assume this takes ~1000 ms
for (let i=0; i<50000000; ++i) {
let obj = new Object();
}
console.log('Long run complete');
Long run complete
100 ms timeout!
This code runs without stopping to the end...
setTimeout(function() {
console.log('100 ms timeout!');
}, 100);
// Let's assume this takes ~1000 ms
for (let i=0; i<50000000; ++i) {
let obj = new Object();
}
console.log('Long run complete');
setTimeout()
causes an asynchronous callback after 100 ms...
setTimeout(function() {
console.log('100 ms timeout!');
}, 100);
// Let's assume this takes ~1000 ms
for (let i=0; i<50000000; ++i) {
let obj = new Object();
}
console.log('Long run complete');
...but this for
loop will block the event loop until it's done.
setTimeout(function() {
console.log('100 ms timeout!');
}, 100);
// Let's assume this takes ~1000 ms
for (let i=0; i<50000000; ++i) {
let obj = new Object();
}
console.log('Long run complete');
Generators allow functions to pause and resume execution!
const one = JSON.parse(fs.readFileSync('data-1.json').toString());
// Do something with the data...
const more = JSON.parse(fs.readFileSync('data-' + one.nextIndex + '.json').toString());
if (more.nextIndex) {
const extra = fs.readFileSync('data-' + extra.nextIndex + '.json');
}
run(function* readAllFiles() {
const one = JSON.parse(yield readFile('data-1.json'));
const more = JSON.parse(yield readFile('data-' + one.nextIndex + '.json'));
const extra = JSON.parse(yield readFile('data-' + more.nextIndex + '.json'));
console.log( one, more, extra );
});
function* foo() {
console.log('Inside foo');
yield 'a';
console.log('Back inside foo');
yield { 'b': 2 };
}
And this is how you execute it...
const genObj = foo();
const one = genObj.next();
console.log('Outside foo');
const two = genObj.next();
function* foo() {
console.log('Inside foo');
yield 'a';
console.log('Back inside foo');
yield { 'b': 2 };
}
And this is how you execute it...
const genObj = foo();
const one = genObj.next();
console.log('Outside foo');
const two = genObj.next();
function* foo() {
console.log('Inside foo');
yield 'a';
console.log('Back inside foo');
yield { 'b': 2 };
}
And this is how you execute it...
const genObj = foo();
const one = genObj.next();
console.log('Outside foo');
const two = genObj.next();
function* foo() {
console.log('Inside foo');
yield 'a';
console.log('Back inside foo');
yield { 'b': 2 };
}
And this is how you execute it...
const genObj = foo();
const one = genObj.next();
console.log('Outside foo');
const two = genObj.next();
function* foo() {
console.log('Inside foo');
yield 'a';
console.log('Back inside foo');
yield { 'b': 2 };
}
And this is how you execute it...
const genObj = foo();
const one = genObj.next();
console.log('Outside foo');
const two = genObj.next();
function* foo() {
console.log('Inside foo');
yield 'a';
console.log('Back inside foo');
yield { 'b': 2 };
}
And this is how you execute it...
const genObj = foo();
const one = genObj.next();
console.log('Outside foo');
const two = genObj.next();
function* foo() {
console.log('Inside foo');
yield 'a';
console.log('Back inside foo');
yield { 'b': 2 };
}
And this is how you execute it...
const genObj = foo();
const one = genObj.next();
console.log('Outside foo');
const two = genObj.next();
And this is how you execute it...
And this is how you execute it...
Inside foo
Outside foo
Back inside foo
Notice that my generator "yielded" two values:
Call the next()
one last time...
{ value: 'a', done: false }
{ value: { b: 2 }, done: false }
{ value: undefined, done: true }
Adding a return
statement:
const genObj = foo();
console.log( genObj.next() );
console.log( genObj.next() );
console.log( genObj.next() );
{ value: 'a', done: false }
{ value: { b: 2 }, done: false }
{ value: 'c', done: true }
In fact, we can iterate (or loop) over them!
a
{ b: 2 }
yield
Receiving input from a yield
We just throw
and catch
Error objects.
function* luckyNumber() {
try {
yield 13;
} catch( err ) {
console.log( 'Oops!', err.message );
}
}
If an error occurs outside the generator...
Oops! I did it again.
If an error occurs inside the generator...
Caught outside generator: Nope nope nope.
Generators (Obviously) and Promises
Easy, just promisify
the function!
function promisify( callbackStyleFn ) {
return function promiseStyleFn(originalArg) {
return new Promise(function( resolve, reject ) {
callbackStyleFn(originalArg, function(err, value) {
if (err) { return reject(err); }
resolve(value);
});
});
};
}
(Warning: do not use this code!)
next()
to start itvalue
off the yielded objectnext()
over and overgen.throw()
We want to abstract away their "Iterable" nature...
next()
done
yield
ed Promise to resolve / rejectfunction run(generator) {
const genObj = generator();
function runGen(result) {
const item = genObj.next(result);
if (!item.done) {
item.value
.then(function(data) {
runGen(data);
})
.catch(function(err) {
genObj.throw(err);
runGen();
});
}
}
runGen();
}
(Warning: do not use this code! It is not complete.)
(Warning: do not use this code! It is not complete.)
(Warning: do not use this code! It is not complete.)
(Warning: do not use this code! It is not complete.)
(Warning: do not use this code! It is not complete.)
(Warning: do not use this code! It is not complete.)
(Warning: do not use this code! It is not complete.)
(Warning: do not use this code! It is not complete.)
(Warning: do not use this code! It is not complete.)
{ "foo": "bar", "nextIndex": 2, "message": "Generators" }
{ "foo": "bat", "nextIndex": 5, "message": "are" }
{ "foo": "baz", "nextIndex": null, "message": "cool" }
{ "foo": "bar", "nextIndex": 2, "message": "Generators" }
100 ms timeout!
{ "foo": "bat", "nextIndex": 5, "message": "are" }
{ "foo": "baz", "nextIndex": null, "message": "cool" }
ERROR! ENOENT, open 'data-infinity.json'
Retrieved data: [ '{ "foo": "bar", "nextIndex": 2 }',
'{ "foo": "bat", "nextIndex": 5 }' ]
async
and await
async function getData() {
try {
const one = JSON.parse(await readFile('data-1.json'));
const two = JSON.parse(await readFile('data-' + one.nextIndex + '.json'));
const three = JSON.parse(await readFile('data-' + two.nextIndex + '.json'));
} catch(err) {
// Handle the error...
}
}
Because it is!
async
functions are based on "pausing" in generators
and await
requires a Promise!
... aaaah, but how long until I can use this?
Jordan Kasper | @jakerella