可以编写node.js的方法,以便同时调用两种方法-即使用回调还是异步/等待?

时间:2020-03-31 16:46:19

标签: node.js promise async-await

这些年来,我创建了一个在项目中使用的标准方法库。他们中的许多人都有回调。我最近了解了Promise和async / await,以使代码读取同步并在早期的回调中喜欢它。

对于正在进行的项目和将来的项目,我想对库中的旧方法使用async / await。是否可以简单地在它们前面添加“异步”,然后在我调用它们时添加“等待”以使它们承诺友好。但是同时我的较旧项目可以使用它们并将它们称为回调吗?

例如:

var checkIfFileExists = function(filename,callback){

    fs.stat(filename, function(err, stat) {

        if(err == null) {

            let jsonObj =   {   'status'        : true,
                                'error'         : false
                            };

        } else if(err.code == 'ENOENT') {

            console.log( 'File ' + filename + ' does not exists');

            let jsonObj = { 'status'        : false,
                            'error'     :   err
                        };

        } else {

            let jsonObj =   { 'status'        : false,
                                'error'     : err
                            };
        }

        callback(jsonObj);


    });

};

对于这种方法,我可以像这样简单地添加'async':

var checkIfFileExists = async function(filename,callback){ .... }

然后我的旧项目将其称为回调:

 checkIfFileExists(fileToCheck, function(jsonResponse){
      // do something with response
 });

,新项目将其称为:

let jsonResponse = await checkIfFileExists(fileToCheck);

2 个答案:

答案 0 :(得分:2)

通过基本包装使用标准回调参数的现有函数以返回诺言的方式,存在各种用于“承诺”库。这可能对您有用。

例如:https://www.npmjs.com/package/es6-promisify

const {promisify} = require("es6-promisify");
const checkIfFileExistsPromise = promisify(checkFileIfExists);

const exists = await checkIfFileExistsPromise(...);

还要注意,fs如今已经在Node.js中内置了基于承诺的API:https://nodejs.org/api/fs.html#fs_fs_promises_api

答案 1 :(得分:1)

首先,即使是较旧的代码也可以使用promises。几乎可以在任何版本的node.js或浏览器中轻松填充承诺,并且调用方可以很好地使用.then().catch()。因此,我的第一个论点是,该是时候向前迈进,而将旧的回调样式留在后面了。使用承诺不会阻止任何人使用您的代码,只会迫使他们将其编程知识转移到正确的十年中。

如果您确实想在同一接口中同时提供回调和Promise选项,则不能仅将async放在函数上并使所有功能正常工作。这不是async所做的。它没有那种超级能力。另外,如果您想与旧版本的node.js兼容,那么该版本也可能不支持async

制作可以调用的任何一种方法的通常方法是检测是否通过了回调,并且代码根据是否通过了回调进行调整,如果满足则返回与代码完成相关的promise没有回调传递,如果传递了回调,则使用回调。

因此,如果您有一个接口checkIfFileExists(...),则可以这样使用它:

 // with callback
 checkIfFileExists("myfile.txt", function(err, exists) {
     if (err) {
         console.log(err);
     } else {
         console.log(exists);
     }
 });

 // with promise
 checkIfFileExists("myfile.txt").then(function(exists) {
     console.log(exists);
 }).catch(function(err) {
     console.log(err);
 });

而且,这是一个实现:

const fs = require('fs');
const promisify = require('util').promisify;

function makePromisify(fn) {
    if (fn._p) {
       return fn._p;
    } else {
       fn._p = promisify(fn);
       return fn._p;
    }
}

// if not called with callback, then returns a promise
function checkIfFileExists(filename, callback) {
    if (!callback) {
        return makePromisify(checkIfFileExists)(filename);
    } else {
        fs.stat(filename, function(err, stats) {
           if (err) {
               if (err.code === 'ENOENT') {
                   callback(null, false);
               } else {
                   callback(err);
               }
           } else {
              callback(null, true);
           }
        });
    }
}

此实现使用util.promisify()(在节点v8.0中添加)自动为您创建了回调接口的承诺版本。如果要支持足够老的版本{.1}甚至不存在的node.js,那么也可以用几行代码手动构建。

出于效率方面的考虑,它在首次使用时将已承诺的功能版本缓存为被调用函数的util.promisify()属性,因此在后续调用中,它可以使用与该功能完全相同的承诺版本。

请注意,我希望先对Promise接口进行优化的设计,因为这是更可能的应用,因为这是Java语言的未来,应该是更常见的用法。但是,对于这样的功能,您需要使用._p模块的fs.promises接口,并且至少假定节点v10.0。


如果您可以假设使用最新的节点v10,则可以使用fs界面来简化此操作,并且可以更简化诺言用法:

fs.promises