簡體   English   中英

如何避免Node.js中異步函數的長時間嵌套

[英]How to avoid long nesting of asynchronous functions in Node.js

我想制作一個頁面來顯示數據庫中的一些數據,因此我創建了一些函數來從數據庫中獲取數據。 我只是Node.js中的新手,據我了解,如果我想在一個頁面中使用所有它們(HTTP響應),則必須將它們全部嵌套:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  getSomeDate(client, function(someData) {
    html += "<p>"+ someData +"</p>";
    getSomeOtherDate(client, function(someOtherData) {
      html += "<p>"+ someOtherData +"</p>";
      getMoreData(client, function(moreData) {
        html += "<p>"+ moreData +"</p>";
        res.write(html);
        res.end();
      });
    });
  });

如果有很多類似的功能,那么嵌套就會成為問題

有辦法避免這種情況嗎? 我想這與如何組合多個異步函數有關,這似乎是基本的東西。

有趣的觀察。 請注意,在JavaScript中,通常可以將內聯匿名回調函數替換為命名函數變量。

下列:

http.createServer(function (req, res) {
   // inline callback function ...

   getSomeData(client, function (someData) {
      // another inline callback function ...

      getMoreData(client, function(moreData) {
         // one more inline callback function ...
      });
   });

   // etc ...
});

可以重寫為如下所示:

var moreDataParser = function (moreData) {
   // date parsing logic
};

var someDataParser = function (someData) {
   // some data parsing logic

   getMoreData(client, moreDataParser);
};

var createServerCallback = function (req, res) {
   // create server logic

   getSomeData(client, someDataParser);

   // etc ...
};

http.createServer(createServerCallback);

但是,除非您打算在其他地方重用回調邏輯,否則讀取內聯匿名函數通常會更容易,如您的示例所示。 這也將使您不必為所有回調找到名稱。

另外請注意,正如@pst在下面的注釋中指出的那樣,如果您要在內部函數中訪問閉包變量,則以上內容將不是簡單的轉換。 在這種情況下,使用內聯匿名函數更為可取。

凱,只需使用這些模塊之一即可。

它將變成這樣:

dbGet('userIdOf:bobvance', function(userId) {
    dbSet('user:' + userId + ':email', 'bobvance@potato.egg', function() {
        dbSet('user:' + userId + ':firstName', 'Bob', function() {
            dbSet('user:' + userId + ':lastName', 'Vance', function() {
                okWeAreDone();
            });
        });
    });
});

變成這個:

flow.exec(
    function() {
        dbGet('userIdOf:bobvance', this);

    },function(userId) {
        dbSet('user:' + userId + ':email', 'bobvance@potato.egg', this.MULTI());
        dbSet('user:' + userId + ':firstName', 'Bob', this.MULTI());
        dbSet('user:' + userId + ':lastName', 'Vance', this.MULTI());

    },function() {
        okWeAreDone()
    }
);

您可以將此技巧用於數組而不是嵌套函數或模塊。

在眼睛上容易得多。

var fs = require("fs");
var chain = [
    function() { 
        console.log("step1");
        fs.stat("f1.js",chain.shift());
    },
    function(err, stats) {
        console.log("step2");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step3");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step4");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step5");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("done");
    },
];
chain.shift()();

您可以將成語擴展到並行流程甚至並行流程鏈:

var fs = require("fs");
var fork1 = 2, fork2 = 2, chain = [
    function() { 
        console.log("step1");
        fs.stat("f1.js",chain.shift());
    },
    function(err, stats) {
        console.log("step2");
        var next = chain.shift();
        fs.stat("f2a.js",next);
        fs.stat("f2b.js",next);
    },
    function(err, stats) {
        if ( --fork1 )
            return;
        console.log("step3");
        var next = chain.shift();

        var chain1 = [
            function() { 
                console.log("step4aa");
                fs.stat("f1.js",chain1.shift());
            },
            function(err, stats) { 
                console.log("step4ab");
                fs.stat("f1ab.js",next);
            },
        ];
        chain1.shift()();

        var chain2 = [
            function() { 
                console.log("step4ba");
                fs.stat("f1.js",chain2.shift());
            },
            function(err, stats) { 
                console.log("step4bb");
                fs.stat("f1ab.js",next);
            },
        ];
        chain2.shift()();
    },
    function(err, stats) {
        if ( --fork2 )
            return;
        console.log("done");
    },
];
chain.shift()();

在大多數情況下,我會同意Daniel Vassallo的觀點。 如果您可以將一個復雜且深度嵌套的函數分解為單獨的命名函數,那么通常是個好主意。 在需要在單個函數中執行的情況下,可以使用許多可用的node.js異步庫之一。 人們提出了許多不同的方法來解決此問題,因此請查看一下node.js模塊頁面,然后看看您的想法。

我自己為此編寫了一個模塊,稱為async.js 使用此,上面的示例可以更新為:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  async.series({
    someData: async.apply(getSomeDate, client),
    someOtherData: async.apply(getSomeOtherDate, client),
    moreData: async.apply(getMoreData, client)
  },
  function (err, results) {
    var html = "<h1>Demo page</h1>";
    html += "<p>" + results.someData + "</p>";
    html += "<p>" + results.someOtherData + "</p>";
    html += "<p>" + results.moreData + "</p>";
    res.write(html);
    res.end();
  });
});

這種方法的優點是,您可以通過將“系列”功能更改為“並行”來快速更改代碼以並行獲取數據。 而且,async.js還將在瀏覽器中運行,因此,如果遇到任何棘手的異步代碼,您可以使用與node.js中相同的方法。

希望這是有用的!

為此,我非常喜歡async.js

該問題通過瀑布命令解決:

瀑布(任務,[回調])

依次運行一個函數數組,每個函數將其結果傳遞給數組中的下一個函數。 但是,如果有任何函數將錯誤傳遞給回調,則不會執行下一個函數,並且會立即以錯誤調用主回調。

爭論

任務-要運行的函數數組,每個函數都傳遞一個必須在完成時調用的回調(err,result1,result2,...)。 第一個參數是錯誤(可以為null),任何其他參數將作為參數傳遞給下一個任務。 callback(err,[results])-在所有功能完成后運行的可選回調。 這將傳遞上一個任務的回調結果。

async.waterfall([
    function(callback){
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback){
        callback(null, 'three');
    },
    function(arg1, callback){
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
    // result now equals 'done'    
});

至於req,res變量,它們將在與包圍整個async.waterfall調用的function(req,res){}相同的范圍內共享。

不僅如此,異步非常干凈。 我的意思是,我改變了很多這樣的情況:

function(o,cb){
    function2(o,function(err, resp){
        cb(err,resp);
    })
}

首先:

function(o,cb){
    function2(o,cb);
}

然后到此:

function2(o,cb);

然后到此:

async.waterfall([function2,function3,function4],optionalcb)

它還允許非常快地從util.js中調用為異步准備的許多預制函數。 只需將您想做的事情鏈接起來,確保可以普遍處理o,cb。 這大大加快了整個編碼過程。

您需要的是一些語法糖。 看看這個:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = ["<h1>Demo page</h1>"];
  var pushHTML = html.push.bind(html);

  Queue.push( getSomeData.partial(client, pushHTML) );
  Queue.push( getSomeOtherData.partial(client, pushHTML) );
  Queue.push( getMoreData.partial(client, pushHTML) );
  Queue.push( function() {
    res.write(html.join(''));
    res.end();
  });
  Queue.execute();
}); 

整齊漂亮,不是嗎? 您可能會注意到html變成了數組。 部分原因是字符串是不可變的,因此與丟棄越來越大的字符串相比,將輸出緩沖在數組中更好。 另一個原因是因為bind的另一個不錯的語法。

示例中的Queue實際上只是一個示例,可以partial實現如下

// Functional programming for the rescue
Function.prototype.partial = function() {
  var fun = this,
      preArgs = Array.prototype.slice.call(arguments);
  return function() {
    fun.apply(null, preArgs.concat.apply(preArgs, arguments));
  };
};

Queue = [];
Queue.execute = function () {
  if (Queue.length) {
    Queue.shift()(Queue.execute);
  }
};

自從發現Async.js以來,我一直深愛它。 它具有async.series函數,可用於避免長時間嵌套。

文檔:


系列(任務,[回調])

依次運行一系列功能,每個功能在上一個功能完成后即可運行。 [...]

爭論

tasks -要運行的函數數組,每個函數都傳遞了一個回調,必須在完成時調用它。 callback(err, [results]) -在所有功能完成后運行的可選回調。 此函數獲取傳遞給數組中使用的回調的所有參數的數組。


這是我們將其應用於您的示例代碼的方法:-

http.createServer(function (req, res) {

    res.writeHead(200, {'Content-Type': 'text/html'});

    var html = "<h1>Demo page</h1>";

    async.series([
        function (callback) {
            getSomeData(client, function (someData) { 
                html += "<p>"+ someData +"</p>";

                callback();
            });
        },

        function (callback) {
            getSomeOtherData(client, function (someOtherData) { 
                html += "<p>"+ someOtherData +"</p>";

                callback(); 
            });
        },

        funciton (callback) {
            getMoreData(client, function (moreData) {
                html += "<p>"+ moreData +"</p>";

                callback();
            });
        }
    ], function () {
        res.write(html);
        res.end();
    });
});

我見過的最簡單的語法糖是節點承諾。

npm安裝節點承諾|| git clone https://github.com/kriszyp/node-promise

使用此方法,可以將異步方法鏈接為:

firstMethod().then(secondMethod).then(thirdMethod);

每個參數的返回值都可以用作下一個參數。

在此完成的工作采用了異步模式,並將其應用於依次調用的3個函數,每個函數在啟動前都等待上一個函數完成-即,使它們同步 關於異步編程的要點是,您可以同時運行多個功能,而不必等待每個功能都完成。

如果getSomeDate()沒有為getSomeOtherDate()提供任何內容,而沒有為getMoreData()提供任何內容,那么為什么不按照js的要求異步調用它們,或者如果它們是相互依賴的(而不是異步的),則將它們寫為單功能?

您無需使用嵌套來控制流程-例如,通過調用一個公共函數來確定每個函數的完成時間,該函數確定所有3個函數何時完成,然后發送響應。

在純Javascript中使用閉包可以輕松避免回調地獄。 下面的解決方案假定所有回調都遵循function(error,data)簽名。

http.createServer(function (req, res) {
  var modeNext, onNext;

  // closure variable to keep track of next-callback-state
  modeNext = 0;

  // next-callback-handler
  onNext = function (error, data) {
    if (error) {
      modeNext = Infinity;
    } else {
      modeNext += 1;
    }
    switch (modeNext) {

    case 0:
      res.writeHead(200, {'Content-Type': 'text/html'});
      var html = "<h1>Demo page</h1>";
      getSomeDate(client, onNext);
      break;

    // handle someData
    case 1:
        html += "<p>"+ data +"</p>";
        getSomeOtherDate(client, onNext);
        break;

    // handle someOtherData
    case 2:
      html += "<p>"+ data +"</p>";
      getMoreData(client, onNext);
      break;

    // handle moreData
    case 3:
      html += "<p>"+ data +"</p>";
      res.write(html);
      res.end();
      break;

    // general catch-all error-handler
    default:
      res.statusCode = 500;
      res.end(error.message + '\n' + error.stack);
    }
  };
  onNext();
});

假設您可以這樣做:

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    var html = "<h1>Demo page</h1>";
    chain([
        function (next) {
            getSomeDate(client, next);
        },
        function (next, someData) {
            html += "<p>"+ someData +"</p>";
            getSomeOtherDate(client, next);
        },
        function (next, someOtherData) {
            html += "<p>"+ someOtherData +"</p>";
            getMoreData(client, next);
        },
        function (next, moreData) {
            html += "<p>"+ moreData +"</p>";
            res.write(html);
            res.end();
        }
    ]);
});

您只需要實現chain()即可將每個函數部分地應用到下一個函數,並立即僅調用第一個函數:

function chain(fs) {
    var f = function () {};
    for (var i = fs.length - 1; i >= 0; i--) {
        f = fs[i].partial(f);
    }
    f();
}

我最近創建了一個名為wait.for的更簡單的抽象,以在同步模式下(基於Fibre)調用異步函數。 它處於早期階段,但可以正常工作。 它是在:

https://github.com/luciotato/waitfor

使用wait.for ,您可以調用任何標准的nodejs異步函數,就好像它是同步函數一樣。

使用wait.for您的代碼可以是:

var http=require('http');
var wait=require('wait.for');

http.createServer(function(req, res) {
  wait.launchFiber(handleRequest,req, res); //run in a Fiber, keep node spinning
}).listen(8080);


//in a fiber
function handleRequest(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  var someData = wait.for(getSomeDate,client);
  html += "<p>"+ someData +"</p>";
  var someOtherData = wait.for(getSomeOtherDate,client);
  html += "<p>"+ someOtherData +"</p>";
  var moreData = wait.for(getMoreData,client);
  html += "<p>"+ moreData +"</p>";
  res.write(html);
  res.end();
};

...或者如果您不想那么冗長(並添加錯誤捕獲)

//in a fiber
function handleRequest(req, res) {
  try {
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.write(
    "<h1>Demo page</h1>" 
    + "<p>"+ wait.for(getSomeDate,client) +"</p>"
    + "<p>"+ wait.for(getSomeOtherDate,client) +"</p>"
    + "<p>"+ wait.for(getMoreData,client) +"</p>"
    );
    res.end();
  }
  catch(err) {
   res.end('error '+e.message); 
  }

};

在所有情況下, getSomeDategetSomeOtherDategetMoreData應該是標准異步函數,最后一個參數是函數callback(err,data)

如:

function getMoreData(client, callback){
  db.execute('select moredata from thedata where client_id=?',[client.id],
       ,function(err,data){
          if (err) callback(err);
          callback (null,data);
        });
}

為了解決這個問題,我寫了nodent( https://npmjs.org/package/nodent ),它無形地預處理了您的JS。 您的示例代碼將變為(異步,實際上-請閱讀文檔)。

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  someData <<= getSomeDate(client) ;

  html += "<p>"+ someData +"</p>";
  someOtherData <<= getSomeOtherDate(client) ;

  html += "<p>"+ someOtherData +"</p>";
  moreData <<= getMoreData(client) ;

  html += "<p>"+ moreData +"</p>";
  res.write(html);
  res.end();
});

顯然,還有許多其他解決方案,但是預處理的優點是運行時開銷極少或沒有,而且得益於源映射的支持,調試也很容易。

我以一種非常原始但有效的方式來做。 例如,我需要與父母和孩子一起建立一個模型,假設我需要為他們做單獨的查詢:

var getWithParents = function(id, next) {
  var getChildren = function(model, next) {
        /*... code ... */
        return next.pop()(model, next);
      },
      getParents = function(model, next) {
        /*... code ... */
        return next.pop()(model, next);
      }
      getModel = function(id, next) {
        /*... code ... */
        if (model) {
          // return next callbacl
          return next.pop()(model, next);
        } else {
          // return last callback
          return next.shift()(null, next);
        }
      }

  return getModel(id, [getParents, getChildren, next]);
}

使用Fibers https://github.com/laverdet/node-fibers,它使異步代碼看起來像同步代碼(無阻塞)

我個人使用這個小包裝程序http://alexeypetrushin.github.com/synchronize我的項目中的代碼示例(每個方法實際上都是異步的,使用異步文件IO)我什至不敢想到,回調或回調會造成混亂異步控制流幫助程序庫。

_update: (version, changesBasePath, changes, oldSite) ->
  @log 'updating...'
  @_updateIndex version, changes
  @_updateFiles version, changesBasePath, changes
  @_updateFilesIndexes version, changes
  configChanged = @_updateConfig version, changes
  @_updateModules version, changes, oldSite, configChanged
  @_saveIndex version
  @log "updated to #{version} version"

Task.js為您提供:

spawn(function*() {
    try {
        var [foo, bar] = yield join(read("foo.json"),
                                    read("bar.json")).timeout(1000);
        render(foo);
        render(bar);
    } catch (e) {
        console.log("read failed: " + e);
    }
});

代替這個:

var foo, bar;
var tid = setTimeout(function() { failure(new Error("timed out")) }, 1000);

var xhr1 = makeXHR("foo.json",
                   function(txt) { foo = txt; success() },
                   function(err) { failure() });
var xhr2 = makeXHR("bar.json",
                   function(txt) { bar = txt; success() },
                   function(e) { failure(e) });

function success() {
    if (typeof foo === "string" && typeof bar === "string") {
        cancelTimeout(tid);
        xhr1 = xhr2 = null;
        render(foo);
        render(bar);
    }
}

function failure(e) {
    xhr1 && xhr1.abort();
    xhr1 = null;
    xhr2 && xhr2.abort();
    xhr2 = null;
    console.log("read failed: " + e);
}

其他人回答后,您說您的問題是局部變量。 似乎很簡單的方法是編寫一個外部函數以包含那些局部變量,然后使用一堆命名內部函數並按名稱訪問它們。 這樣,無論需要鏈接多少個函數,您都只會嵌套兩個深度。

這是我的新手嘗試通過嵌套使用mysql Node.js模塊的嘗試:

function with_connection(sql, bindings, cb) {
    pool.getConnection(function(err, conn) {
        if (err) {
            console.log("Error in with_connection (getConnection): " + JSON.stringify(err));
            cb(true);
            return;
        }
        conn.query(sql, bindings, function(err, results) {
            if (err) {
                console.log("Error in with_connection (query): " + JSON.stringify(err));
                cb(true);
                return;
            }
            console.log("with_connection results: " + JSON.stringify(results));
            cb(false, results);
        });
    });
}

以下是使用命名內部函數的重寫。 外部函數with_connection也可以用作局部變量的持有者。 (這里,我有類似的參數sqlbindingscb ,但是您可以在with_connection定義一些其他局部變量。)

function with_connection(sql, bindings, cb) {

    function getConnectionCb(err, conn) {
        if (err) {
            console.log("Error in with_connection/getConnectionCb: " + JSON.stringify(err));
            cb(true);
            return;
        }
        conn.query(sql, bindings, queryCb);
    }

    function queryCb(err, results) {
        if (err) {
            console.log("Error in with_connection/queryCb: " + JSON.stringify(err));
            cb(true);
            return;
        }
        cb(false, results);
    }

    pool.getConnection(getConnectionCb);
}

我一直在想,也許可以用實例變量創建一個對象,並用這些實例變量代替局部變量。 但是現在我發現使用嵌套函數和局部變量的上述方法更簡單易懂。 取消學習OO需要花費一些時間,看來:-)

所以這是我先前的版本,其中包含對象和實例變量。

function DbConnection(sql, bindings, cb) {
    this.sql = sql;
    this.bindings = bindings;
    this.cb = cb;
}
DbConnection.prototype.getConnection = function(err, conn) {
    var self = this;
    if (err) {
        console.log("Error in DbConnection.getConnection: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    conn.query(this.sql, this.bindings, function(err, results) { self.query(err, results); });
}
DbConnection.prototype.query = function(err, results) {
    var self = this;
    if (err) {
        console.log("Error in DbConnection.query: " + JSON.stringify(err));
        self.cb(true);
        return;
    }
    console.log("DbConnection results: " + JSON.stringify(results));
    self.cb(false, results);
}

function with_connection(sql, bindings, cb) {
    var dbc = new DbConnection(sql, bindings, cb);
    pool.getConnection(function (err, conn) { dbc.getConnection(err, conn); });
}

事實證明, bind可以使用一些優勢。 它使我擺脫了我創建的有些丑陋的匿名函數,除了將自己轉發給方法調用之外,它們並沒有做任何其他事情。 我無法直接傳遞該方法,因為它會涉及到this的錯誤值。 但隨着bind ,我可以指定的值, this是我想要的。

function DbConnection(sql, bindings, cb) {
    this.sql = sql;
    this.bindings = bindings;
    this.cb = cb;
}
DbConnection.prototype.getConnection = function(err, conn) {
    var f = this.query.bind(this);
    if (err) {
        console.log("Error in DbConnection.getConnection: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    conn.query(this.sql, this.bindings, f);
}
DbConnection.prototype.query = function(err, results) {
    if (err) {
        console.log("Error in DbConnection.query: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    console.log("DbConnection results: " + JSON.stringify(results));
    this.cb(false, results);
}

// Get a connection from the pool, execute `sql` in it
// with the given `bindings`.  Invoke `cb(true)` on error,
// invoke `cb(false, results)` on success.  Here,
// `results` is an array of results from the query.
function with_connection(sql, bindings, cb) {
    var dbc = new DbConnection(sql, bindings, cb);
    var f = dbc.getConnection.bind(dbc);
    pool.getConnection(f);
}

當然,這些都不是帶有Node.js編碼的正確JS -我只是花了幾個小時。 但是,也許稍微拋光一下,這項技術就能有所幫助嗎?

async.js為此很好地工作。 我碰到了這篇非常有用的文章,其中舉例說明了async.js的需求和用法: http : //www.sebastianseilund.com/nodejs-async-in-practice

如果您不想使用“ step”或“ seq”,請嘗試“ line”,這是減少嵌套異步回調的簡單函數。

https://github.com/kevin0571/node-line

類似於C#的asyncawait是另一種方式

https://github.com/yortus/asyncawait

async(function(){

    var foo = await(bar());
    var foo2 = await(bar2());
    var foo3 = await(bar2());

}

使用wire您的代碼看起來像這樣:

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});

    var l = new Wire();

    getSomeDate(client, l.branch('someData'));
    getSomeOtherDate(client, l.branch('someOtherData'));
    getMoreData(client, l.branch('moreData'));

    l.success(function(r) {
        res.write("<h1>Demo page</h1>"+
            "<p>"+ r['someData'] +"</p>"+
            "<p>"+ r['someOtherData'] +"</p>"+
            "<p>"+ r['moreData'] +"</p>");
        res.end();
    });
});

就您所知,請考慮使用Jazz.js https://github.com/Javanile/Jazz.js/wiki/Script-showcase

const jj = require('jazz.js');

    // ultra-compat stack
    jj.script([
        a => ProcessTaskOneCallbackAtEnd(a),
        b => ProcessTaskTwoCallbackAtEnd(b),
        c => ProcessTaskThreeCallbackAtEnd(c),
        d => ProcessTaskFourCallbackAtEnd(d),
        e => ProcessTaskFiveCallbackAtEnd(e),
    ]);

我有同樣的問題。 我已經看到節點運行異步函數的主要庫,它們呈現出非自然的鏈接(您需要使用三個或更多方法confs等)來構建代碼。

我花了幾周的時間來開發一種簡單易讀的解決方案。 請嘗試EnqJS 所有意見將不勝感激。

代替:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  getSomeDate(client, function(someData) {
    html += "<p>"+ someData +"</p>";
    getSomeOtherDate(client, function(someOtherData) {
      html += "<p>"+ someOtherData +"</p>";
      getMoreData(client, function(moreData) {
        html += "<p>"+ moreData +"</p>";
        res.write(html);
        res.end();
      });
    });
  });

使用EnqJS:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";

  enq(function(){
    var self=this;
    getSomeDate(client, function(someData){
      html += "<p>"+ someData +"</p>";
      self.return();
    })
  })(function(){
    var self=this;
    getSomeOtherDate(client, function(someOtherData){ 
      html += "<p>"+ someOtherData +"</p>";
      self.return();
    })
  })(function(){
    var self=this;
    getMoreData(client, function(moreData) {
      html += "<p>"+ moreData +"</p>";
      self.return();
      res.write(html);
      res.end();
    });
  });
});

請注意,該代碼似乎比以前更大。 但是它不像以前那樣嵌套。 為了看起來更自然,這些鏈被立即稱為:

enq(fn1)(fn2)(fn3)(fn4)(fn4)(...)

並說它返回了,在我們調用的函數內部:

this.return(response)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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