• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

node如何避免回调地狱?

nodejs 程序猿 4年前 (2021-12-27) 67次浏览 已收录 0个评论

为了解决阻塞问题,js严重依赖于回调,这是在长时间运行的进程(IO,定时器等)完成后运行的函数,因此允许代码执行经过长时间运行的任务。

虽然回调的概念在理论上是巨大的,但它可能导致一些真正令人困惑和难以阅读的代码。 想象一下,如果你需要在回调后进行回调:

getData(function(a){  
    getMoreData(a, function(b){
        getMoreData(b, function(c){ 
            getMoreData(c, function(d){ 
                getMoreData(d, function(e){ 
                    ...
                });
            });
        });
    });
});

你可以看到,这真的是一发不可收拾。 抛出一些if语句,for循环,函数调用或注释,你会有一些非常难读的代码。 初学者特别是这个的受害者,不理解如何避免这个“金字塔的厄运”。

这种层层嵌套的代码给开发带来了很多问题,主要体现在:

代码可能性变差
调试困难
出现异常后难以排查

node避免回调地狱的方法:

1、promise

promise模式在任何时刻都处于以下三种状态之一:未完成(unfulfilled)、已完成(resolved)和拒绝(rejected)。

以CommonJS Promise/A 标准为例,promise对象上的then方法负责添加针对已完成和拒绝状态下的处理函数。then方法会返回另一个promise对象,以便于形成promise管道,这种返回promise对象的方式能够支持开发人员把异步操作串联起来,如:then(resolvedHandler, rejectedHandler);

resolvedHandler 回调函数在promise对象进入完成状态时会触发,并传递结果;rejectedHandler函数会在拒绝状态下调用。(其中rejectedHandler可选)。

以下为一个有几级嵌套的函数,看起来比较令人恶心。(如果换成缩进四个字符可想而知)

'use strict';
const md = require('markdown-it')();
const fs = require('fs');
fs.watchFile('nodejs.md', (curr, prev) => {
  let mdStr = fs.readFile('./nodejs.md', 'utf-8', (err, data) => {
    let mdData = md.render(data);
    let htmlTemplate = fs.readFile('./index.html', 'utf-8', (err, data) => {
      let html = data.replace('{{content}}', mdData);
      console.log(mdData);
      fs.writeFile('./nodejs.html', html, 'utf-8', (err, data) => {
        if (err) {
          throw err;
        } else {
          console.log('OK');
        }
      });
    });
  });
});

以下用promise的方式实现同样的效果,首先把异步函数封装一下,然后下面可以直接调用。可能看起来代码比之前的版本更多,但是封装的异步函数是可以复用的。等任务多了就不显得代码多了。(但看最后调用函数的部分是不是优雅了不少)

'use strict';
const fs = require('fs');
const md = require('markdown-it')();
var Q = require('q');

function fs_readFile(file, encoding) {

  var deferred = Q.defer();
  fs.readFile(file, encoding, function(err, data) {
    if (err) deferred.reject(err); // rejects the promise with `er` as the reason
    else
      deferred.resolve(data) // fulfills the promise with `data` as the value
  });
  return deferred.promise; // the promise is returned
}

function fs_writeFile(file, data, encoding) {
  var deferred = Q.defer();
  fs.writeFile(file, data, encoding, function(err, data) {
    if (err) deferred.reject(err); // rejects the promise with `er` as the reason
    else deferred.resolve(data); // fulfills the promise with `data` as the value
  });
  return deferred.promise ;// the promise is returned
    //return 1; // the promise is returned
}

function fs_watchFile(file, curr, prev) {
  var deferred = Q.defer();
  fs.watchFile(file, function(curr, prev) {
    if (!prev) deferred.reject(err); // rejects the promise with `er` as the reason
    else deferred.resolve(curr); // fulfills the promise with `data` as the value
  });
  return deferred.promise // the promise is returned
}

function markdowm_convert(file, encoding, mdData) {

  var convertData = md.render(mdData);
  console.log(convertData);
  var deferred = Q.defer();
  fs.readFile(file, encoding, function(err, data) {
    if (err) deferred.reject(err); // rejects the promise with `er` as the reason
    else {
      data = data.replace('{{content}}', convertData);
      deferred.resolve(data); // fulfills the promise with `data` as the value
    }
  })
  return deferred.promise; // the promise is returned
}




// ===============promise实现  =====================
fs_watchFile('nodejs.md')
  .then(function() {
    return fs_readFile('./nodejs.md', 'utf-8');
  })
  .then(function(mdData) {
    return markdowm_convert('./index.html', 'utf-8', mdData);
  })
  .then(function(data) {
    fs_writeFile('./nodejs.html', data, 'utf-8');
  });

2、async

node的async包有多的数不清的方法我暂时只实验了一个waterfall

waterfall瀑布流的意思和async中另一个函数series差不多都是按照顺序执行,不同之处是waterfall每执行完一个函数都会产生一个值,然后把这个值给下一个函数用。

以下是嵌套了两级的读写文件程序

fs.readFile('01.txt','utf-8',function(err,date){
  fs.writeFile('02.txt',date,'utf-8',function(err,date){
    console.log('复制完了');
  })<strong style="color:transparent">来源gaodai#ma#com搞@@代~&码网</strong>;
})

用async.waterfall 后代码如下

async.waterfall([
  function(cb){
    fs.readFile('01.txt','utf-8',function(err,result){
      cb(err,result);
    });

  },function(result,cb){
    fs.writeFile('02.txt',result,'utf-8',function(err,result){
      cb(err,result);
    });
  }
  ],function(err,result){
 console.log('复制完了');
})

3、Use modules

在几乎每种编程语言中,降低复杂性的最好方法之一是模块化。 JavaScript也不例外。 每当你编写代码时,花一些时间来回顾一下你是否经常遇到一个常见的模式。

你在不同的地方多次写相同的代码吗? 你的代码的不同部分是否遵循一个共同的主题? 如果是这样,你有机会清理东西,抽象和重用代码。

有数千个模块,你可以看看供参考,但这里有几个要考虑。 它们处理常见的,但非常具体的任务,否则会扰乱你的代码并降低可读性:Pluralize,csv,qs,clone。

下面是一个名为formuploader.js的新文件,其中包含以前的两个函数:

module.exports.submit = formSubmit

function formSubmit (submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, postResponse)
}

function postResponse (err, response, body) {
  var statusMessage = document.querySelector('.status')
  if (err) return statusMessage.value = err
  statusMessage.value = body
}

现在我们有了formuploader.js(它在浏览后作为脚本标记加载到页面中),我们只需要它并使用它!下面是我们的应用程序特定代码的外观:

var formUploader = require('formuploader')
document.querySelector('form').onsubmit = formUploader.submit

以上就是node如何避免回调地狱?的详细内容,更多请关注gaodaima搞代码网其它相关文章!


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:node如何避免回调地狱?

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址