【问题标题】:Node.js right way to use multiple callbacksNode.js 使用多个回调的正确方法
【发布时间】:2018-03-20 05:01:46
【问题描述】:

我是 Node.js 的新手,非常抱歉。

在我的代码中,我使用了某些服务的 API。基本上是很多回调。这些回调提供了我需要在另一个回调中使用的服务值。

如果是同步方式,那么我会这样写:

var value1 = 10;
var value2 = 0;
var value3 = '';
var value4 = '';

api.method1((error, data) => {
    if (error) {
        // some code
    }

    value2 = data.value1FromApi / value1;    
});

if (value2 > 0) {
    api.method2(value2, value1, (error, response) => {
        if (error) {
            // some code
        }

        value3 = response.value2FromApi;
        value4 = response.value3FromApi;
    });

    // Check status
    for (var i = 0; i < 10; i++) {
        if (value4 !== 'some status') {
            api.method3(value3, (error, response) => {
                value4 = response.value4FromApi;
            });

            // wait(1000);
        }
        else {
            break;
        }
    }

    if (value4 !== 'some status') {
        api.method4(value3, (error, response) => {

        });
    }
    else {
        // similar code as above
    }
}

我知道这是错误的代码,因为在第一次回调后value2 将未定义。

告诉我我需要应用什么技术或什么技术来解决这个回调序列?

【问题讨论】:

  • 你可以看看 Promises。取而代之或“返回”,您将解析将从响应中获得的变量或对象。

标签: node.js callback


【解决方案1】:

您需要以异步方式处理此问题。你可以试试这个:

api.method1((error, data) => {
    if (error) {
        // some code
    }

    value2 = data.value1FromApi / value1;
    if (value2 > 0) {
        api.method2(value2, value1, (error, response) => {
            if (error) {
                // some code
            }

            value3 = response.value2FromApi;
            value4 = response.value3FromApi;
            // Check status
            for (var i = 0; i < 10; i++) {
                if (value4 !== 'some status') {
                    api.method3(value3, (error, response) => {
                        value4 = response.value4FromApi;
                        if (value4 !== 'some status') {
                                    api.method4(value3, (error, response) => {

                                    });
                                }
                        else {
                            // similar code as above
                        }
                    });
                }
                else {
                    break;
                }
            }

        });
    }
});

基本上所有的回调都必须相互嵌套

【讨论】:

    【解决方案2】:

    为了链接许多请求,您需要使用promises。

    var method3CalledTimes = 0;
    const api = { // Replace api with real API calls
      method1: function() { 
          return Promise.resolve({ value1FromApi: 1 }); 
      },
      method2: function() { 
          return Promise.resolve({ value2FromApi: 2, value3FromApi: 3 }); 
      },
      method3: function() { 
          if (method3CalledTimes++ <= 3) // for demo
              return Promise.resolve({ status: "not-correct-status-yet" });
          return Promise.resolve({ status: "correct-status" }); 
      },
      method4: function() { 
          return Promise.resolve("Success"); 
      }
    };
    
    var value1 = 10,
        value2, 
        value3, 
        value4;
    
    api.method1()
      .then(function(data) {
        value2 = data.value1FromApi / value1;
        
        if (value2 <= 0) 
          return Promise.reject("v2 is non-positive");      
        
        return api.method2();    
      })
      .then(function(data) {
        value3 = data.value2FromApi;
        value4 = data.value3FromApi;
        return Promise.all([api.method3(), api.method4()]);
      })
      .then(function() {   
        return new Promise(function(resolve, reject) {
          var tryCount = 10;
    
          function checkStatus() 
          {
            console.log("Trying to call method3(). Attempts left: " + tryCount);
            api.method3()
              .then(function(data) {
                console.log("Status response: " + data.status);
                if (data.status === "correct-status") {
                  console.log("Valid status, resolving...");
                  resolve(data.status); // status did change
                } else {
                  if (tryCount-- <= 0) { // if no tries left
                    console.log("Invalid status, no attempts left. Failing...");
                    reject("statusnotchanged"); // status didn't change after N attempts
                  } else { // if we have tries
                    console.log("Invalid status, we have attempts. Trying again...");
                    setTimeout(checkStatus, 1000); // try again in 1 second
                  }
                }
              })
              .catch(reject); // if error - reject
          }    
    
          checkStatus(); // call once, then it will call itself with setTimeout      
        });
      })
      .then(function(newStatus) {
        console.log("Complete! New status = " + newStatus); 
      })
      .catch(function(r) {
        if (reason === "statusnotchanged") {
          // handle this specific reason if needed
        }
        console.error(r);
      });

    此代码只是一个示例,它显示了这个想法。您的代码示例意图非常不清楚,因此我无法对其进行优化。

    看看 Promise 如何让错误处理变得更容易。链中的任何拒绝都将导致.catch 执行。尝试将value1 设置为 0 看看会发生什么。

    【讨论】:

    • 好的!感谢大家Promise。至于循环。我不需要等待 method3 10 次,而是我需要每次循环检查是否更改状态。如果状态发生变化,我需要立即调用方法 4。这可能吗?
    • @Konstantin 您可以使用new Promise() 并实现自定义解析拒绝逻辑。 setTimeout 将帮助您等待结果。请查看更新后的答案。
    • @Konstantin 我已经更新了我的解决方案。出于演示目的,它现在返回前 3 次调用的错误状态,然后返回有效状态。结果,在第一次成功调用后,我们停止尝试并完成执行。
    • Yeldar,告诉我我是对的,我需要在 const 声明中添加真正的服务 api 代码吗?
    • 是的,您需要在那里实现 API 调用。用户 Fix3r 在“外部 API 调用”中提供了示例方法。
    【解决方案3】:

    value2 是 undefined,因为它是在异步函数中分配的。 value2 的使用发生在函数执行之前。

    您可以了解一些关于 JavaScript EventLoop 的知识,以便更好地理解。

    PS。处理异步函数和回调地狱的方法有很多,例如Promise、async、co、async & await in node 8。

    查看http://callbackhell.com/ 了解更多信息。

    【讨论】:

      【解决方案4】:

      我最近在学习node.js时遇到了同样的问题。

      我有一个调用外部 API 的文件,当用户请求路由时,我的服务器将响应作为 json 对象发送。


      外部 API 调用

      var 连接 {user:"user", password:"password"} //这只是我的连接对象的一个​​例子

      exports.getUsers = function(){
          return new Promise(function(resolve, reject){
                var params = {search_string:""};
                var users  = [];
                connection.Users.get(params, function(error, response){
                     var user;
                     for(let i = 0; i < response.data.length; i++)
                     {
                         if(response.data[i].name=="user"
                         {
                             user = response.data[i];
                         }
                     }
                         users.push({user});
                     resolve(users); //return the user object
                  });
              });
           };
      

      NODE.JS 服务器

      const request = require('./requests.js'); //file with my external api calls
      
      app.get('/users', function(req, res){
           requests.getUsers()
           .then(function(users){
                res.contentType('application/json');
                res.send(JSON.stringify(users));
            })
           .catch(function(err){
                console.log(err);
           });
        });
      

      如果您使用 PROMISE,该函数将等到您获得所需的值后再继续。

      。那么您将能够根据需要将该结果发送给调用您的 api 的用户。

      在 MDN 文档中,您绝对可以找到有关 JS 的所有内容,并且非常有用。

      https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise


      编辑

      阅读更多关于承诺的信息,我发现其他链接也很有用: http://exploringjs.com/es6/ch_promises.html

      【讨论】:

        猜你喜欢
        • 2014-01-30
        • 2020-04-13
        • 1970-01-01
        • 2015-01-08
        • 2016-05-09
        • 2023-03-10
        • 1970-01-01
        • 2021-05-16
        • 2018-04-17
        相关资源
        最近更新 更多