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)

近期下载者

相关文件


收藏者