waitfor
所属分类:Node.js
开发工具:JavaScript
文件大小:304KB
下载次数:0
上传日期:2018-10-11 19:05:15
上 传 者:
sh-1993
说明: node.js的顺序编程,结束回调地狱末日金字塔
(Sequential programming for node.js, end of callback hell pyramid of doom)
文件列表:
LICENSE (1080, 2016-08-21)
bin (0, 2016-08-21)
bin\node (909824, 2016-08-21)
examples (0, 2016-08-21)
examples\minimal.js (451, 2016-08-21)
examples\multiple-fibers-on-ReadStream-data.js (2065, 2016-08-21)
examples\waitfor-cradle.js (2543, 2016-08-21)
examples\waitfor-demo.js (2280, 2016-08-21)
package.json (881, 2016-08-21)
parallel-tests.js (2381, 2016-08-21)
tests.js (3421, 2016-08-21)
waitfor.js (7472, 2016-08-21)
Wait.for
=======
Sequential programming for node.js, end of callback hell.
Simple, straightforward abstraction over [Fibers](https://github.com/laverdet/node-fibers).
By using **wait.for**, you can call any nodejs standard async function in sequential/Sync mode, waiting for result data,
without blocking node's event loop (thanks to fibers)
A nodejs standard async function is a function in which the last parameter is a callback: function(err,data)
Advantages:
* Avoid callback hell / pyramid of doom
* Simpler, sequential programming when required, without blocking node's event loop (thanks to fibers)
* Simpler, try-catch exception programming. (default callback handler is: if (err) throw err; else return data)
* You can also launch multiple parallel non-concurrent fibers.
* No multi-threaded debugging nightmares, only one fiber running at a given time (thanks to fibers)
* Can use any node-standard async function with callback(err,data) as last parameter.
* Plays along with node programming style. Write your async functions with callback(err,data), but use them in sequential/SYNC mode when required.
* Plays along with node cluster. You design for one thread/processor, then scale with cluster on multicores.
##NEWS
###Aug-2013 - Wait.for-ES6 based on ES6-generators
I've developed ***a version based on JavaScript upcoming ES6-Harmony generators***. It's not based on node-fibers.
***Surprisingly***, ES6 based implementation of *wait.for(asyncFn)* is almost a no-op, you can even completely omit it. *Warning: Bleeding edge*. Check [Wait.for-ES6] (https://github.com/luciotato/waitfor-ES6)
---------------
Install:
----
```
npm install wait.for
```
Proper Use:
----
You need to be in a Fiber to be able to use wait.for. The ideal place to launch a fiber
is when a request arrives, to handle it:
```js
var server = http.createServer(
function(req, res){
console.log('req!');
wait.launchFiber(handler,req,res); //handle in a fiber, keep node spinning
}).listen(8000);
```
then,at *function handler(req,res)* and every function you call from there,
you'll be able to use wait.for(ayncFn...
Minimal running example
----
```js
var wait = require('wait.for');
function anyStandardAsync(param, callback){
setTimeout( function(){
callback(null,'hi '+param);
}, 5000);
};
function testFunction(){
console.log('fiber start');
var result = wait.for(anyStandardAsync,'test');
console.log('function returned:', result);
console.log('fiber end');
};
console.log('app start');
wait.launchFiber(testFunction);
console.log('after launch');
```
Basic Usage Example with Express.js
----
```js
var wait = require('wait.for');
var express = require('express');
var app = express();
// in a Fiber
function handleGet(req, res){
res.send( wait.for(fs.readFile,'largeFile.html') );
}
app.get('/', function(req,res){
wait.launchFiber(handleGet, req, res); //handle in a fiber, keep node spinning
});
app.listen(3000);
```
Cradle/couchdb Usage Example
----
see [cradle example](/examples/waitfor-cradle.js)
Generic Usage:
------------
```js
var wait=require('wait.for');
// launch a new fiber
wait.launchFiber(my_sequential_function, arg,arg,...)
// in a fiber.. We can wait for async functions
function my_sequential_function(arg,arg...){
// call async_function(arg1), wait for result, return data
var myObj = wait.for(async_function, arg1);
// call myObj.querydata(arg1,arg2), wait for result, return data
var myObjData = wait.forMethod(myObj,'queryData', arg1, arg2);
console.log(myObjData.toString());
}
```
-------------
##Notes on non-standard callbacks. e.g.: connection.query from mysql, database.prepare on node-sqlite3
wait.for expects standardized callbacks.
A standardized callback always returns (err,data) in that order.
A solution for the sql.query method and other non-standard callbacks
is to create a wrapper function standardizing the callback, e.g.:
```js
connection.prototype.q = function(sql, params, stdCallback){
this.query(sql,params, function(err,rows,columns){
return stdCallback(err,{rows:rows,columns:columns});
});
}
```
usage:
```js
try {
var result = wait.forMethod(connection, "q", options.sql, options.params);
console.log(result.rows);
console.log(result.columns);
}
catch(err) {
console.log(err);
}
```
e.g.: node-sqlite3's [database.prepare](https://github.com/mapbox/node-sqlite3/wiki/API)
```js
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database(':memory:');
db.prototype.prep = function(sql, stdCallback){
var stmt = this.prepare(sql, function(err){
return stdCallback(err, stmt);
});
}
var stmt = wait.forMethod (db, 'prep', "INSERT OR REPLACE INTO foo (a,b,c) VALUES (?,?,?)");
```
More Examples:
-
DNS testing, *using pure node.js* (a little of callback hell):
```js
var dns = require("dns");
function test(){
dns.resolve4("google.com", function(err, addresses) {
if (err) throw err;
for (var i = 0; i < addresses.length; i++) {
var a = addresses[i];
dns.reverse(a, function (err, data) {
if (err) throw err;
console.log("reverse for " + a + ": " + JSON.stringify(data));
});
};
});
}
test();
```
***THE SAME CODE***, using **wait.for** (sequential):
```javascript
var dns = require("dns"), wait=require('wait.for');
function test(){
var addresses = wait.for(dns.resolve4,"google.com");
for (var i = 0; i < addresses.length; i++) {
var a = addresses[i];
console.log("reverse for " + a + ": " + JSON.stringify(wait.for(dns.reverse,a)));
}
}
wait.launchFiber(test);
```
Database example (pseudocode)
--
*using pure node.js* (a callback hell):
```js
var db = require("some-db-abstraction");
function handleWithdrawal(req,res){
try {
var amount=req.param("amount");
db.select("* from sessions where session_id=?",req.param("session_id"),function(err,sessiondata) {
if (err) throw err;
db.select("* from accounts where user_id=?",sessiondata.user_ID),function(err,accountdata) {
if (err) throw err;
if (accountdata.balance < amount) throw new Error('insufficient funds');
db.execute("withdrawal(?,?)",accountdata.ID,req.param("amount"), function(err,data) {
if (err) throw err;
res.write("withdrawal OK, amount: "+ req.param("amount"));
db.select("balance from accounts where account_id=?", accountdata.ID,function(err,balance) {
if (err) throw err;
res.end("your current balance is " + balance.amount);
});
});
});
});
}
catch(err) {
res.end("Withdrawal error: " + err.message);
}
}
```
Note: The above code, although it looks like it will catch the exceptions, **it will not**.
Catching exceptions with callback hell adds a lot of pain, and i'm not sure if you will have the 'res' parameter
to respond to the user. If somebody like to fix this example... be my guest.
***THE SAME CODE***, using **wait.for** (sequential logic - sequential programming):
```js
var db = require("some-db-abstraction"), wait=require('wait.for');
function handleWithdrawal(req,res){
try {
var amount=req.param("amount");
sessiondata = wait.forMethod(db,"select","* from session where session_id=?",req.param("session_id"));
accountdata = wait.forMethod(db,"select","* from accounts where user_id=?",sessiondata.user_ID);
if (accountdata.balance < amount) throw new Error('insufficient funds');
wait.forMethod(db,"execute","withdrawal(?,?)",accountdata.ID,req.param("amount"));
res.write("withdrawal OK, amount: "+ req.param("amount"));
balance = wait.forMethod(db,"select","balance from accounts where account_id=?", accountdata.ID);
res.end("your current balance is " + balance.amount);
}
catch(err) {
res.end("Withdrawal error: " + err.message);
}
}
```
Note: Exceptions will be catched as expected.
db methods (db.select, db.execute) will be called with this=db
-------------
##How does wait.launchFiber works?
`wait.launchFiber(genFn,param1,param2)` starts executing the `function genFn` *as a fiber-generator* until a "yield" (wait.for) is found, then `wait.launchFiber` execute the "yielded" value (a call to an async function), and links generator's "next" with the async callback(err,data), so when the async finishes and the callback is called, the fiber/generator "continues" after the `var x =wait.for(...)`.
Parallel Extensions
----------
-------------
###wait.parallel.launch(functions:Array)
Note: must be in a Fiber
####input:
* functions: Array = [[func,arg,arg],[func,arg,arg],...]
wait.parallel.launch expects an array of [[func,arg,arg..],[func,arg,arg..],...] and then launches a fiber for each function call, in parallel, and waits for all the fibers to complete.
The functions to be called ***should not be async functions***.
Each called sync function will be executed in it's own fiber, and this sync function should/can use `data=wait.for(..)` internally in order to call async functions.
####actions:
-launchs a fiber for each func
-the fiber does `resultArray[index] = func.apply(undefined,args)`
####returns:
- array with a result for each function
- do not "returns" until all fibers complete
- throws if error
-------------
###wait.parallel.map(arr:Array, mappedFn:function)
Note: must be in a Fiber
####input:
- arr: Array
- mappedFn = function(item,index,arr)
-- mappedFn should return converted item. Since we're in a fiber
-- mappedFn can use wait.for and also throw/try/catch
####returns:
- array with converted items
- do not "returns" until all fibers complete
- throws if error
-------------
###wait.parallel.filter(arr:Array, itemTestFn:function)
Note: must be in a Fiber
####input:
- arr: Array
- itemTestFn = function(item,index,arr)
-- itemTestFn should return true|false. Since we're in a fiber
-- itemTestFn can use wait.for and also throw/try/catch
####returns
- array with items where itemTestFn() returned true
- do not "returns" until all fibers complete
- throws if error
-------------
Parallel Usage Example:
see:
- [parallel-tests](/parallel-tests.js)
近期下载者:
相关文件:
收藏者: