Книга: Functional Programming in JavaScript
Назад: Maybes
Дальше: Lenses

Promises

The nature of promises is that they remain immune to changing circumstances.

- Frank Underwood, House of Cards

In functional programming, we're often working with pipelines and data flows: chains of functions where each function produces a data type that is consumed by the next. However, many of these functions are asynchronous: readFile, events, AJAX, and so on. Instead of using a continuation-passing style and deeply nested callbacks, how can we modify the return types of these functions to indicate the result? By wrapping them in promises.

Promises are like the functional equivalent of callbacks. Obviously, callbacks are not all that functional because, if more than one function is mutating the same data, then there can be race conditions and bugs. Promises solve that problem.

You should use promises to turn this:

fs.readFile("file.json", function(err, val) {   if( err ) {     console.error("unable to read file");   }   else {     try {       val = JSON.parse(val);       console.log(val.success);     }     catch( e ) {       console.error("invalid json in file");     }   } });

Into the following code snippet:

fs.readFileAsync("file.json").then(JSON.parse)   .then(function(val) {     console.log(val.success);   })   .catch(SyntaxError, function(e) {     console.error("invalid json in file");   })   .catch(function(e){     console.error("unable to read file")   });

The preceding code is from the README for bluebird: a full featured Promises/A+ implementation with exceptionally good performance. Promises/A+ is a specification for implementing promises in JavaScript. Given its current debate within the JavaScript community, we'll leave the implementations up to the Promises/A+ team, as it is much more complex than maybes.

But here's a partial implementation:

// the Promise monad var Promise = require('bluebird');  // the promise functor var promise = function(fn, receiver) {   return function() {     var slice = Array.prototype.slice,     args = slice.call(arguments, 0, fn.length - 1),     promise = new Promise();     args.push(function() {       var results = slice.call(arguments),       error = results.shift();       if (error) promise.reject(error);       else promise.resolve.apply(promise, results);     });     fn.apply(receiver, args);     return promise;   }; };

Now we can use the functor to transform functions that take callbacks into functions that return promises.

var files = ['a.json', 'b.json', 'c.json']; readFileAsync = promise(fs.readFile); var data = files   .map(function(f){     readFileAsync(f).then(JSON.parse)   })   .reduce(function(a,b){     return $.extend({}, a, b)   });
Назад: Maybes
Дальше: Lenses

bsn
thank
Vesa Karvonen
I hope you don't mind, but I’d like to point you and your readers to my high-performance optics library for JavaScript that is in production use in multiple projects, has comprehensive support for partial optics and interactive documentation: https://calmm-js.github.io/partial.lenses/ (this takes a moment to load — be patient!)