[英]Using native ES6 Promises to de-Nodeify / compact standard callbacks
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"]);
在README的“适应节点”部分中将进一步讨论。
当使用本机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);
尽管这确实使最坏情况的回调变得更糟,但它在视觉上仍然混乱不堪并且难以遵循。
显然,可以将其分解为函数并内联更少的代码以使其更具可读性,但是该解决方案对于纠正应许首先要解决的回调回调地狱非常有效。 :-)
我对标准的ES2015 / 6 Promise功能集缺少什么可以使人在这里节省一些麻烦的东西吗? 否则,将建议使用低热量的填充料。
大多数流行的基于回调的程序包都有相应的约定,例如fs-extra
和mz/fs
代表fs
。
pify
是pify
中广泛使用的解决方案,默认情况下使用本机Promise
。 该维护者提供的其他与承诺相关的软件包也可能会有所帮助,例如p-event
可以使一次性事件侦听器成为可能。
http
示例涉及多次触发的回调,这种回调不能被基于Promise的函数替代。 但是显然有可能预先使基于回调的事物像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);
});
})
如果没有第三方承诺解决方案,开发人员将不得不重新发明轮子,这涉及使用new Promise
构建new Promise
,如原始示例所示。
应该注意的是,Bluebird是ES6 Promise的流行替代品,特别是因为它提供了开箱即用的所需功能,包括Promisification 。
从8.0.0版本开始,Node内置了util.promisify
来实现Node风格的回调。 像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) }), {})
);
我想一个人总能发明自己的...
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);
您可以完全避免承诺,而只需通过nsynjs同步执行代码。 您的代码将进行如下转换:
步骤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;
第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);
};
}
步骤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);
请在这里查看类似的示例https://github.com/amaksr/nsynjs/tree/master/examples/node-module-loading
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.