StuQjsNode.js
jspromisegeneratorasyncNode.js
i5ting CTOStuQ Moajs Node.js Node 4 Web Koa
ava
co
yieldable 5
async/await
callback vs hell
Node.jserror-firstEventEmitter
thunk
promise/a+
generator/yield
async/await
xxx-fy
avamocha
JavaScript Node.js IO AVA IO Pageres Mocha AVA 31 11
tjmochaava
test('synchronization', t => {})
test.cb('callback', t => {})
test('promise', t => {})
test('generator function', function * (t) {})
test('async function', async t => {})
$ npm init
$ npm i -D ava
$ npm i -D co-exec
jsajaxsetTimeout
1.js
import test from 'ava';
test('synchronization', t => {
const a = /foo/;
const b = 'bar';
const c = 'baz';
t.false(a.test(b) || b === c);
});
ajaxNode.jsapi
Node.js
callbackapiEventEmitterEventEmitter
2.js
import test from 'ava';
const exec = require('child_process').exec
test.cb('error-first callback with setTimeout', t => {
setTimeout(() => {
t.pass();
t.end();
}, 2000);
});
test.cb('error-first callback with exec', t => {
exec('cat *.js bad_file | wc -l',
function (error, stdout, stderr) {
t.pass();
t.end();
});
});
Promise
PromiseCommon JSPromose/A+
jQueryPromisedefferedpromiseAngularjs$qpromiseq
3.js
import test from 'ava';
// promise
test('promise', t => {
return Promise.resolve(3).then(n => {
t.is(n, 3);
});
});
generatores6generatoryieldyieldyieldgenerator
avagenerator
4.js
import test from 'ava';
var exec = require('co-exec');
test('generatorFn with exec()', function * (t) {
let commit = yield exec('ls -alt|grep .gitignore|wc -l');
t.true(commit == 1);
});
generatornextasync
asynces7 stage-3es7asyncawaityieldawaitpromise
5.js
import test from 'ava';
test('async function', async t => {
const bar = Promise.resolve('bar');
t.is(await bar, 'bar');
});
asynchronous-flow-control git:(master) ava -v *.js
1 synchronization
2 error-first callback with exec
3 promise
4 generatorFn with exec()
5 async function
2 error-first callback with setTimeout (2s)
6 tests passed [09:48:27]
asynchronous-flow-control git:(master) ava -v *.js -s
1 synchronization
2 error-first callback with setTimeout (2s)
2 error-first callback with exec
3 promise
4 generatorFn with exec()
5 async function
6 tests passed [09:48:35]
-s
js6:
*js
jsPromisecallback
promisecommonjspromise/Apromise/A+es6
The Promise object is used for asynchronous computations. A Promise represents an operation that hasn't completed yet, but is expected in the future.
PromisePromise
Node.js 0.129/116.27100%10/11Node.jsPromise0.12
Promise async/await API
node.jspromisebluebirdbenchmark
A promise is an abstraction for asynchronous programming. Its an object that proxies for the return value or the exception thrown by a function that has to do some asynchronous processing. Kris Kowal on JSJ
PromisePromisethenPromisePromisereject
var promise = new Promise(function(resolve, reject) {
// do a thing, possibly async, then
if (/* everything turned out fine */) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
});
apierror-firstapi
async/promise/hello.js
// callbacks
var fs = require("fs");
fs.readFile('./package.json', (err, data) => {
if (err) throw err;
console.log(data.toString());
});
promise
async/promise/hellopromise.js
// callbacks to promise
var fs = require("fs");
function hello (file) {
return new Promise(function(resolve, reject){
fs.readFile(file, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.toString())
}
});
});
}
hello('./package.json').then(function(data){
console.log('promise result = ' + data)
}).catch(function(err) {
console.log(err)
})
Node.jsapipromisefs.readFile
promise
new Promise(function(resolve, reject){
})
hello('./package.json').then(function(data){
})
hello('./package.json').then(function(data){
}).catch(function(err) {
})
Promisecallbackthen
fs.readFile
fs.readFile('./package.json', (err, data) => {
if (err) throw err;
console.log(data.toString());
});
callback
function hello (file) {
...
}
Promise
function hello (file) {
return new Promise(function(resolve, reject){
...
});
}
resolvereject
function hello (file) {
return new Promise(function(resolve, reject){
fs.readFile(file, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.toString())
}
});
});
}
Node.jserror-firstcallbackNode.jsAPIPromise
Promise
PromisethenPromisePromise
// callbacks to promise
var fs = require("fs");
function hello (file) {
return new Promise(function(resolve, reject){
fs.readFile(file, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.toString())
}
});
});
}
function world (file) {
return new Promise(function(resolve, reject){
fs.readFile(file, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.toString())
}
});
});
}
function log(data){
return new Promise(function(resolve, reject){
console.log('promise result = ' + data)
resolve(data)
});
}
hello('./package.json').then(log).then(function(){
return hello('./each.js').then(log)
}).catch(function(err) {
console.log(err)
})
hello``world``log
Promisehello('./each.js').then(log)
Promise
promosethenthenPromise.prototypePromise
Promise.prototype.then = function(sucess, fail) {
this.done(sucess);
this.fail(fail);
return this;
};
thisthen
then2
sucess
Promisepending, fulfilled rejected.
pendingfulfillpengdingrejectfulfilledreject
Promise
pending,promise
fulfilled,promise
rejected,promise
identity
PromisePromisethen
Promise
new Promise(function(resolve, reject){
})
fsreflow.js
way 1
hello('./package.json').then(function(data){
console.log('way 1:\n')
return new Promise(function(resolve, reject){
console.log('promise result = ' + data)
resolve(data)
});
}).then(function(data){
return new Promise(function(resolve, reject){
resolve('1')
});
}).then(function(data){
console.log(data)
return new Promise(function(resolve, reject){
reject(new Error('reject with custom err'))
});
}).catch(function(err) {
console.log(err)
})
thenpromiseresolverejectpromise
way 2
hello('./package.json').then(function(data){
console.log('\n\nway 2:\n')
return new Promise(function(resolve, reject){
console.log('promise result = ' + data)
resolve(data)
}).then(function(data){
return new Promise(function(resolve, reject){
resolve('1')
});
}).catch(function(err) {
console.log(err)
})
}).then(function(data){
console.log(data)
return new Promise(function(resolve, reject){
reject(new Error('reject with custom err'))
});
}).catch(function(err) {
console.log(err)
})
thenthen
way 3
refact
var step1 = function(data){
console.log('\n\nway 3:\n')
return new Promise(function(resolve, reject){
console.log('promise result = ' + data)
resolve(data)
}).then(function(data){
return new Promise(function(resolve, reject){
resolve('1')
});
}).catch(function(err) {
console.log(err)
})
}
var step2 = function(data){
console.log(data)
return new Promise(function(resolve, reject){
reject(new Error('reject with custom err'))
});
}
hello('./package.json').then(step1).then(step2).catch(function(err) {
console.log(err)
})
Promise
way 4
final
: require-directory
commonjsrequirerequirerequire-directory
commonjs
reflow/tasks/index.js
var requireDirectory = require('require-directory');
module.exports = requireDirectory(module);
reflow/tasks
commonjs
reflow/tasks/hello.js
var fs = require("fs");
module.exports = function hello (file) {
return new Promise(function(resolve, reject){
fs.readFile(file, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.toString())
}
});
});
}
module.exports
step1step2
var tasks = require('./tasks')
tasks.hello('./package.json').then(tasks.step1).then(tasks.step2).catch(function(err) {
console.log(err)
})
require('./tasks')
tasksPromise
step1step2
var tasks = require('./tasks')
tasks.hello('./package.json').then(tasks.step2).then(tasks.step1).catch(function(err) {
console.log(err)
})
catch
promise.then(function(result) {
console.log('Got data!', result);
}).catch(function(error) {
console.log('Error occurred!', error);
});
then
promise.then(function(result) {
console.log('Got data!', result);
}).then(undefined, function(error) {
console.log('Error occurred!', error);
});
thenreject
promisetry/catchtry/catchTODO:
try {
throw new Error('never will know this happened')
} catch (e) {}
promises
readFile()
.then(function (data) {
throw new Error('never will know this happened')
})
errors.then(null, onRejected)
readFile()
.then(function (data) {
throw new Error('now I know this happened')
})
.then(null, console.error)
errorQdoneerror
api/catch.js
var p1 = new Promise(function(resolve, reject) {
resolve('Success');
});
p1.then(function(value) {
console.log(value); // "Success!"
return Promise.reject('oh, no!');
}).catch(function(e) {
console.log(e); // "oh, no!"
// return Promise.reject('oh, no! 2');
}).then(function(){
console.log('after a catch the chain is restored');
}, function () {
console.log('Not fired due to the catch');
});
$ node api/catch.js
Success
oh, no!
after a catch the chain is restored
api/catch2.js
var p1 = new Promise(function(resolve, reject) {
resolve('Success');
});
p1.then(function(value) {
console.log(value); // "Success!"
return Promise.reject('oh, no!');
}).catch(function(e) {
console.log(e); // "oh, no!"
return Promise.reject('oh, no! 2');
}).then(function(){
console.log('after a catch the chain is restored');
}, function () {
console.log('Not fired due to the catch');
});
$ node api/catch2.js
Success
oh, no!
Not fired due to the catch
PromisePromise
[^1]: based on es6-promise, so excluded from the registery by default
Promise
Q Promises Deferreds 2009Node.jsIO API Q-IO
Promise promisepromise
Q Bluebird API referenceBluebirdBluebird
QQDeferredjQueryDeferred Coming from jQuery
BluebirdPromise Promise
tj: bluebird is MASSIVE, why not use v8's?
bluebirdNode.jsapiPromise
bluebird
$ npm i -S bluebird
hellopromise-bb.js
// callbacks to promise
var fs = require("fs");
var Promise = require("bluebird");
function hello (file) {
return new Promise(function(resolve, reject){
fs.readFile(file, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.toString())
}
});
});
}
hello('./package.json').then(function(data){
console.log('promise result = ' + data)
}).catch(function(err) {
console.log(err)
})
hellopromise.js
var Promise = require("bluebird");
Node.jsPromisebluebird0
var``Promise
koaexpressweb
globalapp.js
global.Promise = require("bluebird");
Node.js native promise bluebird bluebird
Promisification means converting an existing promise-unaware API to a promise-returning API.
bluebirdpromisefypromisifyAll
promisifyAllpromisifypromisify
var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));
fs.readFileAsync("./package.json", "utf8").then(function(contents) {
console.log(contents);
}).catch(function(e) {
console.error(e.stack);
});
abc 3bluebirdpromisifyAllpromise
var Promise = require("bluebird");
var obj = {
a: function(){
console.log('a')
},
b: function(){
console.log('b')
},
c: function(){
console.log('c')
}
}
Promise.promisifyAll(obj);
obj.aAsync().then(obj.bAsync()).then(obj.cAsync()).catch(function(err){
console.log(err)
})
promisifyAllerrorbluebird
1
new Promise( /* executor */ function(resolve, reject) { ... } );
Promise2resolvereject
resolvePromise.resolverejectPromise.reject
promise/api/a.js
new Promise(function(resolve){
resolve(1);
}).then(function(value){
console.log('new Promise ' + value);
});
Promise.resolve(1).then(function(value){
console.log('Promise.resolve ' + value);
});
2resolvePromise.resolve
promise/api/b.js
var error = new Error('this is a error')
new Promise(function(resolve, reject){
reject(error);
}).catch(function(err){
console.log('new Promise ' + err);
});
Promise.reject(error).catch(function(err){
console.log('Promise.resolve ' + err);
});
2rejectPromise.reject
resolverejectpromise/api/c.js
//
new Promise(function(){
return Promise.resolve(1)
}).then(function(value){
console.log('Promise.resolve 1 ' + value);
});
Node.jsPromise
promise/api/d.js
//
function hello(i){
return Promise.resolve(i)
}
hello(1).then(function(value){
console.log('Promise.resolve 1 ' + value);
});
Promise.resolvePromisenew Promise(resolve, reject)
PromisePromise
i
promise/api/e.js
//
function hello(i){
if (i % 2 == 0) {
return Promise.resolve(i)
} else {
return Promise.reject(i)
}
}
hello(1).then(function(value){
console.log('Promise.reject 1 ' + value);
});
hello(2).then(function(value){
console.log('Promise.resolve 1 ' + value);
});
Promise.resolvePromise.reject
Promise.resolve(value); Promise.resolve(promise); Promise.resolve(thenable);
Promise.reject(reason);
2Promise.prototype.then()
p.then(onFulfilled, onRejected);
p.then(function(value) { // fulfillment }, function(reason) { // rejection });
3Promise.prototype.catch()
p.catch(onRejected);
p.catch(function(reason) { // rejection });
4
Promise.all promise FulFilled Rejected Promise.race promise FulFilled Rejected
allthenraceoncethen
promise
all.js
'use strict'
let sleep = (time, info) => {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(info)
resolve('this is ' + info)
}, time)
})
}
let loser = sleep(1000, 'loser')
let winner = sleep(4, 'winner')
// main
Promise.all([winner, loser]).then(value => {
console.log("then: " + value) // => 'this is winner'
})
$ node api/all.js
winner
loser
then: this is winner,this is loser
race.js
'use strict'
let sleep = (time, info) => {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(info)
resolve('this is ' + info)
}, time)
})
}
let loser = sleep(1000, 'loser')
let winner = sleep(4, 'winner')
// main
Promise.race([winner, loser]).then(value => {
console.log("then: " + value) // => 'this is winner'
})
$ node api/race.js
winner
then: this is winner
loser
https://github.com/calvinmetcalf/lie/blob/master/lib/index.js
es6generatortjcocogeneratoryieldyieldable
cogenerator
:
generator
function* doSomething() {
console.log('1');
yield; // Line (A)
console.log('2');
}
var gen1 = doSomething();
gen1.next(); // Prints 1 then pauses at line (A)
gen1.next(); // resumes execution at line (A), then prints 2
yieldnext
tjcogeneratorcov4Promise
co : The ultimate generator based flow-control goodness for nodejs (supports thunks, promises, etc)
var co = require('co');
co(function *(){
// yield any promise
var result = yield Promise.resolve(true);
})
coco2api
var fn = co.wrap(function* (val) {
return yield Promise.resolve(val);
});
fn(true).then(function (val) {
});
//
function co(gen) {
// this
var ctx = this;
var args = slice.call(arguments, 1)
// we wrap everything in a promise to avoid promise chaining,
// which leads to memory leak errors.
// see https://github.com/tj/co/issues/180
// coPromisethencatch
return new Promise(function(resolve, reject) {
// Promise
onFulfilled();
/**
* @param {Mixed} res
* @return {Promise}
* @api private
*/
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
/**
* @param {Error} err
* @return {Promise}
* @api private
*/
// Promise
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
// generator
// ret.doneret.value
//
function next(ret) {
// resolvepromise
if (ret.done) return resolve(ret.value);
// yieldpromise
// promisegeneratorgeneratorFunctionarrayobject
// toPromisepromise
var value = toPromise.call(ctx, ret.value);
// promiseonFulfilled, onRejectedpromisenext
// generatornext
// return value.then(onFulfilled, onRejected);onFulfilled
// onFulfillednext(ret);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
//
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}
});
}
onFulfillednext(ret)generatornext()
next(ret)onFulfilledonRejectednext(ret)
// generator
// ret.doneret.value
//
function next(ret) {
// resolvepromise
if (ret.done) return resolve(ret.value);
// yieldpromise
// promisegeneratorgeneratorFunctionarrayobject
// toPromisepromise
var value = toPromise.call(ctx, ret.value);
// promiseonFulfilled, onRejectedpromisenext
// generatornext
// return value.then(onFulfilled, onRejected);onFulfilled
// onFulfillednext(ret);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
//
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}
1:
// resolvepromise
if (ret.done) return resolve(ret.value);
2 nextonFulfilled
// promiseonFulfilled, onRejectedpromisenext
// generatornext
// return value.then(onFulfilled, onRejected);onFulfilled
// onFulfillednext(ret);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
3:
//
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
co2apigeneratorwrap
// generator
co.wrap = function (fn) {
createPromise.__generatorFunction__ = fn;
return createPromise;
function createPromise() {
// argumentsfn
// callapplyjs api
return co.call(this, fn.apply(this, arguments));
}
};
callapply
yieldablegeneratoryieldyield6yieldyieldableyield
PromisethunksPromisethenableco v4.6Promise
generatorgeneratorFunctionyieldyield*koa 1.x2.x
"" Thunk
thunk?
JavaScript Thunk
// readFile
fs.readFile(fileName, callback);
// ThunkreadFile
var readFileThunk = Thunk(fileName);
readFileThunk(callback);
var Thunk = function (fileName){
return function (callback){
return fs.readFile(fileName, callback);
};
};
fs readFile Thunk
Thunk Thunk
var Thunk = function(fn){
return function (){
var args = Array.prototype.slice.call(arguments);
return function (callback){
args.push(callback);
return fn.apply(this, args);
}
};
};
fs.readFile Thunk
var readFileThunk = Thunk(fs.readFile);
readFileThunk(fileA)(callback);
co(function* () {
var res = yield [
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3),
];
console.log(res); // => [1, 2, 3]
}).catch(onerror);
co(function* () {
var res = yield {
1: Promise.resolve(1),
2: Promise.resolve(2),
};
console.log(res); // => { 1: 1, 2: 2 }
}).catch(onerror);
Any generator or generator function you can pass into co can be yielded as well. This should generally be avoided as we should be moving towards spec-compliant Promises instead.
koa 1.x
app.use(function *(next){
var start = new Date;
yield next;
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
});
koa 2.x
app.use(co.wrap(function *(ctx, next) {
const start = new Date();
yield next();
const ms = new Date() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
}));
JavaScript
Promise Generator
I/Ogeneratoryieldable
generatorco
coYieldable
async/await
ES7 stage-3 async/await es7Async coreadme.md
async Promise await Promise resolve
async function a1() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
})
}
async function a2() {
await a1();
console.log("2333");
}
a2()
async / awaitF#C# 4.0MSDN
PromisePromisethen
await / asynctry..catchtry..catchJavaScriptPromisereject
2copromisehack
async function a2() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
})
}
async function a1() {
console.log("hello a1 and start a2");
await a2();
console.log("hello end a2");
}
async function a0() {
console.log("hello a0 and start a1");
await a1();
console.log("hello end a1");
}
a0()
awaitPromise
var asyncFn = async function () {
return new Promise(function (resolve, reject) {
try {
await somecall();
resolve(somevalue);
} catch (err) {
reject(err);
}
});
};
console.log(await asyncFn());
yield2yieldarrayobjectAsyncawaitPromise
awaitPromisePromisePromise2apiallrace
async/array.js
'use strict'
let sleep = (time, info) => {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(info)
resolve('this is ' + info)
}, time)
})
}
let loser = sleep(1000, 'loser')
let winner = sleep(4, 'winner')
async function main() {
await Promise.all([winner, loser]).then(value => {
console.log("then: " + value) // => 'this is winner'
})
console.log("hello world");
}
main()
$ runkoa array.js
array.js
3babel presets path = /Users/sang/.nvm/versions/node/v4.4.5/lib/node_modules/runkoa/node_modules/
winner
loser
then: this is winner,this is loser
hello world
await Promise.all([])
raceall
awaitPromise
coyield function, promise, generator, array, or object
yieldyieldableyield
co Promise
await co(xxxx)
asyncPromiseco
Async
ChakraCore\Chromegenerator
ChakraawaityieldyieldawaittokenJs::OpCode::YieldChakraawaitParserByteCodeEmitter::Emit
case knopAwait:
case knopYield:
byteCodeGenerator->StartStatement(pnode);
funcInfo->AcquireLoc(pnode);
Emit(pnode->sxUni.pnode1, byteCodeGenerator, funcInfo, false);
EmitYield(pnode->sxUni.pnode1->location, pnode->location, byteCodeGenerator, funcInfo);
funcInfo->ReleaseLoc(pnode->sxUni.pnode1);
byteCodeGenerator->EndStatement(pnode);
break;
awaityield
chromenodenodeV8v8ecma 262
Mikeal Rogersv8Async10
Node.js AsyncgeneratornativeAsyncKoa 2.xAsyncweb
Node.jstry/catcherror-firstasyncawaittry/catch
try {
console.log(await asyncFn());
} catch (err) {
console.error(err);
}
Promise
webdaopromiseserviceasync/await
daopromise
promisefyAll
servicemodelawait
****Promise
****Async + Promise
AVAKoa 2.x Node 4 Web Koa StuQKoastuqkoa