简体   繁体   English

使用本机ES6 Promises取消节点化/压缩标准回调

[英]Using native ES6 Promises to de-Nodeify / compact standard callbacks

The Q library ( https://github.com/kriskowal/q ) provides very helpful adaptors for functions that follow Node's standard callback layout, ie last argument is function(err, result) . Q库( https://github.com/kriskowal/q )为遵循Node的标准回调布局的函数提供了非常有用的适配器,即,最后一个参数是function(err, result)

return Q.nfcall(FS.readFile, "foo.txt", "utf-8");
return Q.nfapply(FS.readFile, ["foo.txt", "utf-8"]);

That's discussed further in the "Adapting Node" section of the README. 在README的“适应节点”部分中将进一步讨论。

When using native ES6 Promises to accomplish the same, one often ends up with this unwieldy kind of trainwreck: 当使用本机ES6 Promises完成相同任务时,通常会遇到这种笨拙的火车残骸:

const fs = require('fs');
const http = require('http');

const server = http.createServer((req, res) => {
    new Promise((resolve, reject) => { 
        fs.readFile('/etc/motd', (err, data) => {
            if(err) { 
                reject(err.toString());
                return;
            }

            resolve(data);
        });
    }).then(data => {
        res.writeHead(200);
        res.end(data);
    }).catch(e => {
        res.writeHead(500);
        res.end(e);
    });
}).listen(8000);

While this does flatten worst-case callback hell, it's still visually cluttered and hard to follow. 尽管这确实使最坏情况的回调变得更糟,但它在视觉上仍然混乱不堪并且难以遵循。

Obviously, one could decompose this into functions and inline less code to make it more readable, but that solution works fairly well for rectifying the very callback hell that promises are supposed to help solve in the first place. 显然,可以将其分解为函数并内联更少的代码以使其更具可读性,但是该解决方案对于纠正应许首先要解决的回调回调地狱非常有效。 :-) :-)

Is there anything I'm missing about the standard ES2015/6 Promise feature set that could allow one to save some mess here? 我对标准的ES2015 / 6 Promise功能集缺少什么可以使人在这里节省一些麻烦的东西吗? Failing that, suggestions for low-calorie polyfills would be appreciated. 否则,将建议使用低热量的填充料。

Browsers and Node.js 7 and lower 浏览器和Node.js 7及更低版本

Most popular callback-based packages have their promisified counterparts, eg fs-extra and mz/fs for fs . 大多数流行的基于回调的程序包都有相应的约定,例如fs-extramz/fs代表fs

pify is widely known solution in for promisification that uses native Promise by default. pifypify中广泛使用的解决方案,默认情况下使用本机Promise Other promise-related packages from this maintainer can be helpful as well, for example p-event to promisify one-time event listeners. 该维护者提供的其他与承诺相关的软件包也可能会有所帮助,例如p-event可以使一次性事件侦听器成为可能。

http example involves a callback that is triggered multiple times, something that cannot be replaced with a promise-based function. http示例涉及多次触发的回调,这种回调不能被基于Promise的函数替代。 But it's obviously possible to promisify callback-based things beforehand like fs (as shown in pify-fs package ): 但是显然有可能预先使基于回调的事物像fs一样(如pify-fs package中所示 ):

const pify = require('pify');
const fs = pify(require('fs'), {
  exclude: [/^exists/, /.+(Stream|Sync)$/, /watch/],
  excludeMain: true
});
...
http.createServer((req, res) => {
    let code;
    let body;

    fs.readFile('/etc/motd')
    .then(
      data => {
        body = data;
        code = 200;
      },
      err => {
        body = String(err);
        code = 500;
      }
    )
    .then(() => {
      res.writeHead(code);
      res.end(body);
    });
})

Without third-party promisification solution the developer is forced to reinvent the wheel, this involves promise construction with new Promise , like shown in original example. 如果没有第三方承诺解决方案,开发人员将不得不重新发明轮子,这涉及使用new Promise构建new Promise ,如原始示例所示。

It should be noticed that Bluebird is a popular alternative to ES6 promises particularly because it provides demanded features out of the box, including promisification . 应该注意的是,Bluebird是ES6 Promise的流行替代品,特别是因为它提供了开箱即用的所需功能,包括Promisification

Node.js 8 Node.js 8

As of 8.0.0, Node has built-in util.promisify to promisify Node-style callbacks. 从8.0.0版本开始,Node内置了util.promisify来实现Node风格的回调。 A recipe for batch promisification of a plain object like fs is fs这样的简单对象的批量promisification的配方是

const util = require('util');
const fs = Object.assign({}, require('fs'),
    Object.entries(require('fs'))
    .filter(([, val]) => typeof val === 'function')
    .filter(([key]) => !/^[A-Z_]|^exists|.+(Stream|Sync)$|watch/.test(key))
    .reduce((fs, [key, val]) => Object.assign(fs, { [key]: util.promisify(val) }), {})
);

I guess one can always invent one's own ... 我想一个人总能发明自己的...

const fs = require('fs');
const http = require('http');

function promiseAdapt(func, ...args) {
    return new Promise((resolve, reject) => {
        func.apply(this, args.concat((err, res) => {
            if(err) {
                reject(err.toString());
                return;
            }

            resolve(res || undefined);
        }));
    });
}

const server = http.createServer((req, res) => {
    let data = undefined;

    promiseAdapt(fs.readFile, '/etc/motd', { 
        encoding: 'utf8' 
    }).then(d => {
        data = d;

        let str = `${req.method} ${req.url} (${req.headers['user-agent'] || '?'}) from ${req.connection.remoteAddress}` + "\n";

        return promiseAdapt(fs.writeFile, 'access.log', str, {
            encoding: 'utf8',
            flag: 'a',
            mode: '0755'
        });
    }).then(() => {
        res.writeHead(200);
        res.end(data);
     }).catch(e => {
        res.writeHead(500);
        res.end(e);
    });
}).listen(8000);

You can avoid promises altogether and just execute your code synchronously via nsynjs . 您可以完全避免承诺,而只需通过nsynjs同步执行代码。 Your code will transform as follows: 您的代码将进行如下转换:

Step 1. Wrap slow functions with callbacks into nsynjs-aware wrappers: 步骤1.将带有回调的慢速函数包装到可感知nsynjs的包装器中:

// wrappers.js

var fs=require('fs');
exports.readFile = function (ctx,name) {
    console.log("reading config");
    var res={};
    fs.readFile( name, "utf8", function( error , data){
        if( error ) res.error = error;
        res.data = data;
        ctx.resume(error);
    } );
    return res;
};
exports.readFile.nsynjsHasCallback = true;

Step 2: Write your logic as if it was synchronous, and put it into function: 第2步:编写逻辑,就好像它是同步的一样,然后将其放入函数中:

const synchronousCode = function(req,res,wrappers) {
    try {
        var data = wrappers.readFile(nsynjsCtx,'/etc/motd').data;
        res.writeHead(200);
        res.end(data);
    }
    catch(e) {
        res.writeHead(500);
        res.end(e);
    };
}

Step 3. Execute that function via nsynjs: 步骤3.通过nsynjs执行该功能:

// index.js
const fs = require('fs');
const http = require('http');
const nsynjs = require('nsynjs');
const wrappers = require('./wrappers');

const synchronousCode = function(req,res,wrappers) {
    ...
};

const server = http.createServer(function(req, res){
    nsynjs.run(synchronousCode,{},req,res,wrappers,function(){
        console.log('synchronousCode is done');
    })
}).listen(8000);

Please see similar example here https://github.com/amaksr/nsynjs/tree/master/examples/node-module-loading 请在这里查看类似的示例https://github.com/amaksr/nsynjs/tree/master/examples/node-module-loading

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM