basic concepts
Coroutine: multiple threads cooperate with each other to complete asynchronous tasks
A coroutine is a bit like a function, but also a bit like a thread. Its running process is roughly as follows:
In the first step, the coroutine A starts to execute.
In the second step, the execution of coroutine A is halfway through, enters a pause, and the execution right is transferred to coroutine B.
In the third step, (after a period of time) Coroutine B returns the execution right.
In the fourth step, the coroutine A resumes execution.
The coroutine of the above processA, Which is an asynchronous task, because itDivide into two (or more) executions
function * asyncJob () {
//... other code
var f = yield readFile(fileA);
//... other code
}
Copy code
Coroutine encounter
Encapsulation of asynchronous tasks
although
var fetch = require ( 'node-fetch' );
function * gen () {
var url = 'https://api.github.com/users/github' ;
var result = yield fetch(url);
console .log(result.bio);
}
var g = gen();
var result = g.next();
result.value.then( function ( data ) {
return data.json();
}).then( function ( data ) {
g.next(data);
});
Copy code
Execute first
The Thunk function is a way to automatically execute the Generator function
"Call by value"
That is, before entering the function body, calculate the value of x + 5 (equal to 6), and then pass this value into the function f. The C language uses this strategy.
var x = 1 ;
function f ( m ) {
return m * 2 ;
}
F (X + . 5 )
//a call-by time, equivalent to
F ( . 6 )
Copy the code
"Call by name"
That is, directly pass the expression x + 5 into the function body, and evaluate it only when it is used. The Haskell language uses this strategy.
var x = 1 ;
function f ( m ) {
return m * 2 ;
}
F (X + . 5 )
//when invoked by name, is equivalent to
(X + . 5 ) * 2
duplicated code
The meaning of Thunk function, an implementation strategy of "call by name"
The "call by name" implementation of the compiler is often
function f ( m ) {
return m * 2 ;
}
f(x + 5 );
//Equivalent to
var thunk = function () {
return x + 5 ;
};
function f ( thunk ) {
return thunk() * 2 ;
}
Copy code
Thunk function of JavaScript language
1,JavaScript The language is called by value, its Thunk Function meaning is different
2. InJavaScriptLanguage,Thunk What the function replaces is not an expression, butMulti-parameter function, Replace it with aOnly accept callback functions as parametersofSingle parameter function
3. Thunk of js: It is equivalent to dividing the function that needs to pass multiple parameters (one of which is cb) into three executions
The first execution of Thunk receives the function to be executed (fn) and returns a function
The second execution passes parameters other than cb (...args)
The third execution only passes cb
//The normal version of readFile (multi-parameter version)
fs.readFile(fileName, callback);
//Thunk version of readFile (single parameter version)
var Thunk = function ( fileName ) {
return function ( callback ) {
return fs.readFile(fileName, callback);
};
};
//Call Thunk to return a function that only accepts cb
var readFileThunk = Thunk(fileName);
//Recall only need to pass in cb
readFileThunk(callback);
Copy code
Trunk function encapsulation
Any function, as long as the parameter has a callback function, can be written in the form of a Thunk function
//ES5 version
var Thunk = function ( fn ) {
return function () {
var args = Array .prototype.slice.call( arguments );
return function ( callback ) {
args.push(callback);
return fn.apply( this , args);
}
};
};
//ES6 version
const Thunk = function ( fn ) {
return function ( ...args ) {
return function ( callback ) {
//Because aegs is an array, it needs to be destructured and assigned when calling
return fn.call( this , .. .args, callback);
//or
//args.push(callback)
//return fn.apply(this, args);
}
};
};
var readFileThunk = Thunk(fs.readFile);
readFileThunk(fileA)(callback);
Copy code
Example
const Thunk = function ( fn ) {
return function ( ...args ) {
return function ( callback ) {
//Because aegs is an array, it needs to be destructured and assigned when calling
return fn.call( this , ...args, callback );
//or
//args.push(callback)
//return fn.apply(this, args);
}
};
};
function f ( a, cb ) {
cb(a);
}
const ft = Thunk(f); //Form a non-destroyed scope
//ft is the first return function
//function (){
//var args = Array.prototype.slice.call(arguments);
//console.log(args);
//return function (callback){
//args.push(callback);
//return fn.apply(this, args);
//}
//};
ft( 1 )( console .log)
//1
//ft(1) gets the second return function that only accepts cb
//function (callback){
//args.push(callback);
//return fn. apply(this, args);
//}
//ft(1)(console.log) is equivalent to
f( 1 , console .log)
Copy code
Thunkify module
For converters in the production environment, it is recommended to use the Thunkify module.
The first is installation.
$ Npm install thunkify copy the code
The usage is as follows (
var thunkify = require ( 'thunkify' );
var fs = require ( 'fs' );
var read = thunkify(fs.readFile);
read( 'package.json' )( function ( err, str ) {
//...
});
//Equivalent to
fs.readFile(fileName, cb)
Copy code
The source code of Thunkify mainly has a check mechanism, the variable called ensures that the callback function is only run once.
var thunkify = require ( 'thunkify' );
function f ( a, b, callback ) {
var sum = a + b;
callback(sum);
callback(sum);
}
var ft = thunkify(f);
var print = console .log.bind( console );
ft( 1 , 2 )(print);
//3 cb only executes the
copy code once
Generator function process management
function * gen () {
//...
}
var g = gen();
var res = g.next();
while (!res.done){
console .log(res.value);
res = g.next();
}
Copy code
In the above code,
var fs = require ( 'fs' );
var thunkify = require ( 'thunkify' );
var readFileThunk = thunkify(fs.readFile);
//Each yield of the Generator function must be an asynchronous Thunk function
var gen = function * () {
var r1 = yield readFileThunk( '/etc/fstab' );
console .log(r1.toString());
var r2 = yield readFileThunk( '/etc/shells' );
console .log(r2.toString() );
};
var g = gen();
var r1 = g.next();
//r1={value:[Function],done:false}
//Call value to pass in the callback function to execute the callback function
r1.value( function ( err, data ) {
if (err) throw err;
var r2 = g.next(data);
r2.value( function ( err, data ) {
if (err) throw err;
g.next(data);
});
});
Copy code
If you look carefully at the code above, you can find
Automatic process management of Thunk function
The real power of the Thunk function is that it can
With this actuator, execute
The premise is that every asynchronous operation must be a Thunk function, that is, the Thunk function that follows the yield command must be
function run(fn) { var gen = fn(); function next(err, data) { var result = gen.next(data); if (result.done) return; result.value(next); } next(); } function* g() { //... } run(g); Copy code
Complete example
//Thunk function
var Thunk = function ( fn ) {
return function () {
var args = Array .prototype.slice.call( arguments );
return function ( callback ) {
args.push(callback);
return fn.apply( this , args);
}
};
};
//Call Thunk to generate the function after the first return
var readFileThunk = Thunk(fs.readFile);
//Function g encapsulates n asynchronous file reading operations
var g = function * () {
var f1 = yield readFileThunk( 'fileA' );
var f2 = yield readFileThunk( 'fileB' );
//...
var fn = yield readFileThunk( 'fileN' );
};
//Automatically execute the Generator function
function run ( fn ) {
//Call the Generator function
var gen = fn();
function next ( err, data ) {
var result = gen.next(data);
if (result.done) return ;
result.value(next);
}
next();
}
run(g);
Copy code
Of the above code
Internal
co module
A small tool for
Below is a
var gen = function * () {
var f1 = yield readFile( '/etc/fstab' );
var f2 = yield readFile( '/etc/shells' );
console .log(f1.toString());
console .log(f2.toString() );
};
Copy code
var co = require ( 'co' );
co(gen);
Copy code
In the above code,
co(gen).then( function () {
console .log( 'Generator function execution completed' );
});
Copy code
Asynchronous completion of the realization of the return of execution rights
(1) Callback function. Wrap the asynchronous operation intoThunk Function, return the right of execution in the callback function
(2)PromiseObject. Wrap the asynchronous operation intoPromise Object, usethenMethod to return the right to execute
Automatic execution based on Promise objects
1. 1. wrap the readFile method of the fs module into a Promise object
var fs = require ( 'fs' );
var readFile = function ( fileName ) {
return new Promise ( function ( resolve, reject ) {
fs.readFile(fileName, function ( error, data ) {
if (error) return reject(error);
resolve(data);
});
});
};
var gen = function * () {
var f1 = yield readFile( '/etc/fstab' );
var f2 = yield readFile( '/etc/shells' );
console .log(f1.toString());
console .log(f2.toString() );
};
Copy code
2. Then, manually execute the above Generator function
var g = gen();
g.next().value.then( function ( data ) {
g.next(data).value.then( function ( data ) {
g.next(data);
});
});
Copy code
3. Write an automatic actuator based on manual execution.
function run ( gen ) {
var g = gen();
function next ( data ) {
var result = g.next(data);
//Check whether the current step is the last step of the Generator function, and return if it is.
if (result.done) return result.value;
//Use the then method to add a callback function to the return value, and then call the next function again.
result.value.then( function ( data ) {
next(data);
});
}
next();
}
run(gen);
Copy code
Handling concurrent asynchronous operations
At this time, to
//How to write an array
co( function * () {
var res = yield [
Promise .resolve( 1 ),
Promise .resolve( 2 )
];
console .log(res);
}).catch(onerror);
//The writing of the object
co( function * () {
var res = yield {
1 : Promise .resolve( 1 ),
2 : Promise .resolve( 2 ),
};
console .log(res);
}).catch(onerror);
Copy code
Example
//Using array loop
co( function * () {
var values = [n1, n2, n3];
yield values.map(somethingAsync);
});
function * somethingAsync ( x ) {
//do something async
return y
}
Copy code
The above code allows three concurrent
Processing Stream
NodeprovideStreamMode reads and writes data, the characteristics areonceData-onlyPart of, Data is divided intoBlock by block,just like"data flow"same. This forProcessing large-scale dataVery advantageous.
- data event: The next data block is ready.
- End event: The entire "data stream" has been processed.
- error event: An error occurred.
use
const co = require ( 'co' );
const fs = require ( 'fs' );
const stream = fs.createReadStream( './les_miserables.txt' );
let valjeanCount = 0 ;
co( function *() {
while ( true ) {
const res = yield Promise .race([
new Promise ( resolve => stream.once( 'data' , resolve)),
new Promise ( resolve => stream.once( 'end' , resolve) ),
new Promise ( ( resolve, reject ) => stream.once( 'error', reject))
]);
if (!res) {
break ;
}
stream.removeAllListeners( 'data' );
stream.removeAllListeners( 'end' );
stream.removeAllListeners( 'error' );
valjeanCount += (res.toString().match( /valjean/ig ) || []).length;
}
console .log( 'count:' , valjeanCount); //count: 1120
});
Copy code
The above code uses