You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					197 lines
				
				5.2 KiB
			
		
		
			
		
	
	
					197 lines
				
				5.2 KiB
			| 
											3 years ago
										 | # gensync
 | ||
|  | 
 | ||
|  | This module allows for developers to write common code that can share | ||
|  | implementation details, hiding whether an underlying request happens | ||
|  | synchronously or asynchronously. This is in contrast with many current Node | ||
|  | APIs which explicitly implement the same API twice, once with calls to | ||
|  | synchronous functions, and once with asynchronous functions. | ||
|  | 
 | ||
|  | Take for example `fs.readFile` and `fs.readFileSync`, if you're writing an API | ||
|  | that loads a file and then performs a synchronous operation on the data, it | ||
|  | can be frustrating to maintain two parallel functions. | ||
|  | 
 | ||
|  | 
 | ||
|  | ## Example
 | ||
|  | 
 | ||
|  | ```js | ||
|  | const fs = require("fs"); | ||
|  | const gensync = require("gensync"); | ||
|  | 
 | ||
|  | const readFile = gensync({ | ||
|  |   sync: fs.readFileSync, | ||
|  |   errback: fs.readFile, | ||
|  | }); | ||
|  | 
 | ||
|  | const myOperation = gensync(function* (filename) { | ||
|  |   const code = yield* readFile(filename, "utf8"); | ||
|  | 
 | ||
|  |   return "// some custom prefix\n" + code; | ||
|  | }); | ||
|  | 
 | ||
|  | // Load and add the prefix synchronously: | ||
|  | const result = myOperation.sync("./some-file.js"); | ||
|  | 
 | ||
|  | // Load and add the prefix asynchronously with promises: | ||
|  | myOperation.async("./some-file.js").then(result => { | ||
|  | 
 | ||
|  | }); | ||
|  | 
 | ||
|  | // Load and add the prefix asynchronously with promises: | ||
|  | myOperation.errback("./some-file.js", (err, result) => { | ||
|  | 
 | ||
|  | }); | ||
|  | ``` | ||
|  | 
 | ||
|  | This could even be exposed as your official API by doing | ||
|  | ```js | ||
|  | // Using the common 'Sync' suffix for sync functions, and 'Async' suffix for | ||
|  | // promise-returning versions. | ||
|  | exports.myOperationSync = myOperation.sync; | ||
|  | exports.myOperationAsync = myOperation.async; | ||
|  | exports.myOperation = myOperation.errback; | ||
|  | ``` | ||
|  | or potentially expose one of the async versions as the default, with a | ||
|  | `.sync` property on the function to expose the synchronous version. | ||
|  | ```js | ||
|  | module.exports = myOperation.errback; | ||
|  | module.exports.sync = myOperation.sync; | ||
|  | ```` | ||
|  | 
 | ||
|  | 
 | ||
|  | ## API
 | ||
|  | 
 | ||
|  | ### gensync(generatorFnOrOptions)
 | ||
|  | 
 | ||
|  | Returns a function that can be "await"-ed in another `gensync` generator | ||
|  | function, or executed via | ||
|  | 
 | ||
|  | * `.sync(...args)` - Returns the computed value, or throws. | ||
|  | * `.async(...args)` - Returns a promise for the computed value. | ||
|  | * `.errback(...args, (err, result) => {})` - Calls the callback with the computed value, or error. | ||
|  | 
 | ||
|  | 
 | ||
|  | #### Passed a generator
 | ||
|  | 
 | ||
|  | Wraps the generator to populate the `.sync`/`.async`/`.errback` helpers above to | ||
|  | allow for evaluation of the generator for the final value. | ||
|  | 
 | ||
|  | ##### Example
 | ||
|  | 
 | ||
|  | ```js | ||
|  | const readFile = function* () { | ||
|  |   return 42; | ||
|  | }; | ||
|  | 
 | ||
|  | const readFileAndMore = gensync(function* (){ | ||
|  |   const val = yield* readFile(); | ||
|  |   return 42 + val; | ||
|  | }); | ||
|  | 
 | ||
|  | // In general cases | ||
|  | const code = readFileAndMore.sync("./file.js", "utf8"); | ||
|  | readFileAndMore.async("./file.js", "utf8").then(code => {}) | ||
|  | readFileAndMore.errback("./file.js", "utf8", (err, code) => {}); | ||
|  | 
 | ||
|  | // In a generator being called indirectly with .sync/.async/.errback | ||
|  | const code = yield* readFileAndMore("./file.js", "utf8"); | ||
|  | ``` | ||
|  | 
 | ||
|  | 
 | ||
|  | #### Passed an options object
 | ||
|  | 
 | ||
|  | * `opts.sync` | ||
|  | 
 | ||
|  |   Example: `(...args) => 4` | ||
|  | 
 | ||
|  |   A function that will be called when `.sync()` is called on the `gensync()` | ||
|  |   result, or when the result is passed to `yield*` in another generator that | ||
|  |   is being run synchronously. | ||
|  | 
 | ||
|  |   Also called for `.async()` calls if no async handlers are provided. | ||
|  | 
 | ||
|  | * `opts.async` | ||
|  | 
 | ||
|  |   Example: `async (...args) => 4` | ||
|  | 
 | ||
|  |   A function that will be called when `.async()` or `.errback()` is called on | ||
|  |   the `gensync()` result, or when the result is passed to `yield*` in another | ||
|  |   generator that is being run asynchronously. | ||
|  | 
 | ||
|  | * `opts.errback` | ||
|  | 
 | ||
|  |   Example: `(...args, cb) => cb(null, 4)` | ||
|  | 
 | ||
|  |   A function that will be called when `.async()` or `.errback()` is called on | ||
|  |   the `gensync()` result, or when the result is passed to `yield*` in another | ||
|  |   generator that is being run asynchronously. | ||
|  | 
 | ||
|  |   This option allows for simpler compatibility with many existing Node APIs, | ||
|  |   and also avoids introducing the extra even loop turns that promises introduce | ||
|  |   to access the result value. | ||
|  | 
 | ||
|  | * `opts.name` | ||
|  | 
 | ||
|  |   Example: `"readFile"` | ||
|  | 
 | ||
|  |   A string name to apply to the returned function. If no value is provided, | ||
|  |   the name of `errback`/`async`/`sync` functions will be used, with any | ||
|  |   `Sync` or `Async` suffix stripped off. If the callback is simply named | ||
|  |   with ES6 inference (same name as the options property), the name is ignored. | ||
|  | 
 | ||
|  | * `opts.arity` | ||
|  | 
 | ||
|  |   Example: `4` | ||
|  | 
 | ||
|  |   A number for the length to set on the returned function. If no value | ||
|  |   is provided, the length will be carried over from the `sync` function's | ||
|  |   `length` value. | ||
|  | 
 | ||
|  | ##### Example
 | ||
|  | 
 | ||
|  | ```js | ||
|  | const readFile = gensync({ | ||
|  |   sync: fs.readFileSync, | ||
|  |   errback: fs.readFile, | ||
|  | }); | ||
|  | 
 | ||
|  | const code = readFile.sync("./file.js", "utf8"); | ||
|  | readFile.async("./file.js", "utf8").then(code => {}) | ||
|  | readFile.errback("./file.js", "utf8", (err, code) => {}); | ||
|  | ``` | ||
|  | 
 | ||
|  | 
 | ||
|  | ### gensync.all(iterable)
 | ||
|  | 
 | ||
|  | `Promise.all`-like combinator that works with an iterable of generator objects | ||
|  | that could be passed to `yield*` within a gensync generator. | ||
|  | 
 | ||
|  | #### Example
 | ||
|  | 
 | ||
|  | ```js | ||
|  | const loadFiles = gensync(function* () { | ||
|  |   return yield* gensync.all([ | ||
|  |     readFile("./one.js"), | ||
|  |     readFile("./two.js"), | ||
|  |     readFile("./three.js"), | ||
|  |   ]); | ||
|  | }); | ||
|  | ``` | ||
|  | 
 | ||
|  | 
 | ||
|  | ### gensync.race(iterable)
 | ||
|  | 
 | ||
|  | `Promise.race`-like combinator that works with an iterable of generator objects | ||
|  | that could be passed to `yield*` within a gensync generator. | ||
|  | 
 | ||
|  | #### Example
 | ||
|  | 
 | ||
|  | ```js | ||
|  | const loadFiles = gensync(function* () { | ||
|  |   return yield* gensync.race([ | ||
|  |     readFile("./one.js"), | ||
|  |     readFile("./two.js"), | ||
|  |     readFile("./three.js"), | ||
|  |   ]); | ||
|  | }); | ||
|  | ``` |