簡體   English   中英

使用本機ES6 Promises取消節點化/壓縮標准回調

[英]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功能集缺少什么可以使人在這里節省一些麻煩的東西嗎? 否則,將建議使用低熱量的填充料。

瀏覽器和Node.js 7及更低版本

大多數流行的基於回調的程序包都有相應的約定,例如fs-extramz/fs代表fs

pifypify中廣泛使用的解決方案,默認情況下使用本機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

Node.js 8

從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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM