【问题标题】:readable async codeflow in node js节点js中的可读异步代码流
【发布时间】:2016-02-21 15:17:58
【问题描述】:

我是 node js 中的异步编程的新手,想知道如何将我以前的(同步)编程转换为 node js 中的可读异步编程。尤其是这个例子:

function getDefaultValue1() {
    return getDefaultValue1FromDB();
};
function getDefaultValue2() {
    return getDefaultValue2FromDB();
};

function doSomething(var optionalValue1, var optionalValue2){
    try {
        if(optionalValue1===null){
            optionalValue1 = getDefaultValue1();
        }
        if(optionalValue2===null){
            optionalValue2 = getDefaultValue2();
        }
        var resultList = [];
        for(var i = 0; i < optionalValue2; i++) {
            resultList.push(functionWithDbCall(optionalValue1, i));
        }
        return doSomeCalculations(resultList);
    }catch(var e){
        //log exception
        return null
    }
};

请注意,节点 js 中的所有 DB-Calls 都是异步的(getDefaultValue1FromDB(var callback)、functionWithDbCall(var optionalValue1, var i, var callback))。

我的想法是: 将回调参数添加到 getDefaultValue => 需要在回调中和 if 之后使用相同的代码。 For 循环在回调被调用之前退出,并且回调需要处理它们被调用的频率并检查以正确的顺序推送项目。将函数更改为使用异步方法需要在调用它的任何地方更改代码,例如将 getDefaultValue 从返回 const int 更改为 DB 调用。

我被困在这里,我发现在为某些库或不可读代码做广告的地方比 asm 或 regex 更糟糕(只写一次,再也不会读/触摸)。我是否错过了我的 node js 学习资料中的一些基本内容,或者这是该语言的设计失败?

所以我希望有人可以告诉我如何用异步节点js编写这个简单的可读函数。

【问题讨论】:

  • 异步代码一点也不难读。你只是不习惯而已。自 1999 年以来,我一直在用 Tcl 编写异步代码,所以当 ajax 出现时,我想:嘿,这很熟悉!当 node.js 出现时也发生了同样的事情,我想:嘿,这就像在浏览器中编码!您不习惯的代码/设计模式在开始时总是显得很难/不可读。如果你去 PICLIST 论坛,你会发现那些因为 for 循环和 if/else 令人困惑而不会接触 C 的人——他们只在汇编中编程。
  • 问题是,异步代码很快。这就是使 javascript 快速的原因,它在 Python 中很快,在 C 中甚至更快(我以前的工作是在诺基亚维护用 C 编写的数据库代码,使用异步 I/O 以获得电信公司期望从电话目录中获得的吞吐量)。如果您拒绝习惯异步代码,您将永远无法利用它的优势。另一方面,一旦你得到它,你就可以用任何语言编写异步代码。 Javascript 只会使异步代码具有高度可读性。尝试使用在 C 中的 while 循环中实现为 switch-case 语句的状态机来实现它,它会更加混乱。
  • @slebetman 我习惯于使用 pthreads et alii 进行异步编程,使用多线程和互斥锁,每个程序执行 1-10 次。在节点 js 中,每个函数执行 1-10 次。像 if(x){A()};B(); 这样的代码变为 if(x){A(B)};B(); (重复 B)和 for 循环中的有序异步之类的东西真的让我大吃一惊。它完全颠倒了代码流、思考和可读性(你突然每个 for 循环有 5 行样板代码。即使 asm 也有简单的代码流。很好,我使用了通常用 C 语言编程的 XC866,只有 asm 中的中断表,但只有 256字节 RAM ;)
  • 因为想学对,所以问别人怎么做,而不是一直做错。
  • 多线程编程和异步编程不一样。异步编程几乎 100% 的时间都是单线程的(有一些例外,比如 node.js 磁盘 I/O)。在大学里我学会了:线程和select()。那当然是在 C 语言中——当时 js 还没有出现在我们的集体雷达上。

标签: javascript node.js asynchronous


【解决方案1】:

我建议使用 Bluebird:

var Promise = require('bluebird');

/**
 * Lets assume getDefaultValue1FromDB() passes a callback fn;
 * @return {Promise}
 */
function getDefaultValue1() {
    return new Promise(function(resolve, reject){
        return getDefaultValue1FromDB(function(err, value){
            if (err) return reject(err);
            return resolve(value);
        });
    });
}

/**
 * Lets assume getDefaultValue2FromDB() passes a callback fn;
 * @return {Promise}
 */
function getDefaultValue2() {
    return new Promise(function(resolve, reject){
        return getDefaultValue2FromDB(function(err, value){
            if (err) return reject(err);
            return resolve(value);
        });
    });
}

/**
 * @param  {Promise|int} optionalValue1
 * @param  {Prommise|int} optionalValue2
 * @return {Promise}
 */
function doSomething(optionalValue1, optionalValue2){

    if(optionalValue1 === null)
        optionalValue1 = getDefaultValue1;
    if(optionalValue2 === null)
        optionalValue2 = getDefaultValue2;

    return Promise
      .all([optionalValue1, optionalValue2])
      .spread(function(val1, val2){
          var resultList = [];
          for(var i = 0; i < val2; i++) {
              resultList.push(functionWithDbCall(val1, i));
          }
          return doSomeCalculationsAsync(resultList);
      }).catch(function(err){
          //Log any errors here.
      });

}

/**
 * Calls the db and returns a promise with the result.
 * @return {Promise}     [description]
 */
function functionWithDbCall(val, i) {
  Return new Promise();
}

/**
 * @param  {[Promise]} list Array of promises.
 * @return {Promise}
 */
function doSomeCalculationsAsync(list) {
    return Promise.all(list).then(doSomeCalculations);
}

请注意,doSomething 将返回一个承诺,因此您必须添加一个 .then() 来处理结果。还假设functionWithDbCall也返回了一个Promise,然后你可以将所有这些放在一个Promise.all()中,然后进行计算。

【讨论】:

  • bluebird 是遇到的库之一 - 除了 async.js。所以只有外部库才有可读的代码?我想我没有在问题中明确表示它是异步时的 functionWithDbCall(val1, i, callback) 还是您的意思是使用与 getDefaultValue1 中相同的模式?
  • 你可以在 es6 的 node 中使用原生 Promise,但不知道它们是否相同。我编辑了答案,您可以将 doSomeCalculations 包装在 doSomeCalculationsAsync 中,在这里您将传递 functionWithDbCall 返回的所有承诺,一旦获得所有承诺,继续计算。
  • 我是否必须将代码中的 Promise 视为返回值?因为您在函数中使用了它们而没有 .then() 。如何访问该值?
  • 你可以有一个promise数组,即:[returnsAPromise(), Promise.resolve("foo"), 250, "foo"],实际上"foo"这样的值会被认为是一个resolved的promise,比如Promise.resolve("foo")。然后,您可以将该数组传递给Promise.all()Promise.each() 等等。我建议你阅读蓝鸟docs
【解决方案2】:

您可能希望使用 ES7 提案中的 async/await - JavaScript 的未来版本。请参阅this answer,了解如何从回调迁移到 Promise 到 async/await。

要在今天实际运行未来的代码,您需要使用Babel 将其编译为当前支持的功能。另外它可以做a lot more而不是异步/等待。

【讨论】:

  • 我希望提案能尽快敲定并尽快合并,因为我的一个简单 express 应用程序的 node_modules 文件夹已经有 30MB 并且需要一分钟才能启动,因为需要数百个模块。尽管对于像我这样缺乏面向回调的语言的人来说,返回语句看起来会产生误导,但它看起来非常干净。稍后我会仔细研究。
猜你喜欢
  • 1970-01-01
  • 2017-01-24
  • 2014-09-18
  • 2018-09-03
  • 2017-08-06
  • 2020-03-30
  • 2015-08-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多