了解Node.JS使用async.waterfall如何执行外部函数
我需要使用async.js模块执行asynchronous函数。 但是当我执行外部函数时,我遇到了一些问题。
代码通过。
但是当我更改全局variables为本地variables,我不能设置使用参数。
var async = require('async'); var ogs = require('open-graph-scraper'); // global variables var param1 = {url: 'http://www.google.com/'}; var param2 = {url: 'https://www.yahoo.com/'}; function function1(callback){ ogs(param1, function(error, data1) { callback(null, data1); }); } function function2(data1, callback){ ogs(param2, function(error, data2) { callback(null, data1, data2); }); } function function3(data1, data2, callback){ console.log(data1); console.log("---------------"); console.log(data2); } (function temp() { async.waterfall([function1, function2, function3], function(err, result){ console.log(result); console.log(err); if(err) console.log(err); } ); })();
如果param1和param2更改为局部variables,像这样..
(function temp() { var param1 = {url: 'http://www.google.com/'}; var param2 = {url: 'https://www.yahoo.com/'}; async.waterfall([function1, function2, function3], function(err, result){ console.log(result); console.log(err); if(err) console.log(err); } ); })();
如何在function1()或function2()中使用“param”
我不能改变本地的functiontypes
async.waterfall([ function(callback){ }, function(data,callback){ }], function(err){ if(err) console.log(err); } );
嘘! 我正在使用一些ES6语法,请至less在节点6上运行代码片段,好吗?
一个asynchronous任务可以被build模为一个需要callback的函数:
function task(arg1, arg2, callback) { // ... callback(null, result); } task(arg1, arg2, (err, result) => { handle(result); });
但是有一个替代的惯例经常简化事情:
function task(arg1, arg2) { // ... return Promise.resolve(result); } task(arg1, arg2).then(handle(result));
虽然这两个约定都是有道理的,但我已经看到第二种方法在实践中更有用,可以编写具有良好error handling的简单asynchronous代码。
最重要的要点是:
- 函数返回一个值; asynchronous任务返回未来价值的承诺。
- 当asynchronous任务完成时,它将该承诺标记为“已解决”。
- 如果asynchronous任务失败,而不是引发exception,则将该承诺标记为“被拒绝”,同时出现错误。
- 返回的承诺也是一个对象。 甚至在它完成之前,你可以用它做有用的事情。 运行asynchronous任务的代码不必与对任务结果感兴趣的代码位于同一位置。
承诺的一个重要的事情是,它们保证是asynchronous的,不像callback:
// callbacks myTask(1, 2, (err, result) => { console.log("A"); }); console.log("B"); // can be AB or BA depending on myTask // promises myTask(1, 2).then(() => { console.log("A"); }) console.log("B"); // always BA
这使代码更容易推理,但这也意味着,当你真正依赖第二个行为时,promise不会有帮助。
(阅读关于诺言的更多信息!)
初始点
好的,让我们回到你的代码。 首先,让我用一个虚拟的asynchronous函数replaceogs
,这样我们就可以在不联网的情况下使用一些代码:
var async = require('async'); function ogs(param, callback) { let value = ["ogs", param]; setTimeout( () => callback(null, value), 20); } // global variables var param1 = {url: 'http://www.google.com/'}; var param2 = {url: 'https://www.yahoo.com/'}; function function1(callback){ ogs(param1, function(error, data1) { callback(null, data1); }); } function function2(data1, callback){ ogs(param2, function(error, data2) { callback(null, data1, data2); }); } function function3(data1, data2, callback){ console.log(data1); console.log("---------------"); console.log(data2); } (function temp() { async.waterfall([function1, function2, function3], function(err, result){ console.log(result); console.log(err); if(err) console.log(err); } ); })();
让我们试试这些承诺的事情
相当于返回承诺而不是callback的ogs
可能如下所示:
function ogs(param, callback) { // return a promise that resolves after 20ms return new Promise((resolve, reject) => { setTimeout(() => { let value = ["ogs", param]; resolve(value); }, 20); }); }
因为ogs
现在可以返回一个promise,所以在每个function
使用它是很简单的:
function function1(){ return ogs(param1); // call async task, obtain the promise for its result and return it directly } function function2() { return ogs(param2); } function function3(data1, data2){ console.log(data1); console.log("---------------"); console.log(data2); }
如果您想在中间添加一些日志logging,那也很简单:
function function2() { return ogs(param2).then(data2 => { console.log("inside function2", data2); return data2; }); }
现在,每个步骤都是一个承诺返回的asynchronous任务,让我们把它们连接在一起! 最简单的方法是使用Promise.then
直接:
(function temp() { function1().then(data1 => { return function2().then(data2 => { return function3(data1, data2); }); }).catch(error => { console.error("There was a problem:", error); }) })();
这将运行function1
,完成后,它将结果传递给function2
,然后将两个结果传递给function3
。
并行运行的东西
可是等等! function2
甚至不需要等待function1
完成。 这是两个单独的请求。 我们可以同时启动他们两个。
(function temp() { let data1Promise = function1(); let data2Promise = function2(); Promise.all([data1Promise, data2Promise]).then(([data1, data2]) => { return function3(data1, data2); }).catch(error => { console.error("There was a problem:", error); }) })();
Promise.all
接受一组promise,并返回一个用一组结果解决的promise。 我们从数组中解压缩这些结果并将它们传递给function3
。
并行运行networking请求应允许您的应用运行速度更快。 赢得!
现在回到你原来的问题:
如何摆脱全局?
我们完全控制了function1
和function2
的签名,所以让我们使用它! 让我们有这些函数把参数作为参数,而不是看全局variables。 喜欢这个:
function function1(param){ return ogs(param); } function function2(param) { return ogs(param, {"some other options": true}); }
这些function现在看起来超级相似! 也许你可以使用一个(或者直接删除它们并直接调用ogs
)?
删除全局variables之后,我们的代码如下所示:
(function temp() { let param1 = {url: 'http://www.google.com/'}; let param2 = {url: 'https://www.yahoo.com/'}; let data1Promise = function1(param1); let data2Promise = function2(param2); Promise.all([data1Promise, data2Promise]).then(([data1, data2]) => { return function3(data1, data2); }).catch(error => { console.error("There was a problem:", error); }) })();
但是我真的需要我的function顺序运行!
如果function2
实际上无法启动没有function1
的结果呢?
function function1(param) { return ogs(param); } function function2(data1, param) { return ogs(param2, {"some other options": data1}); }
我们可以恢复到嵌套的第一个版本,但我们也可以尝试一些更加整洁:
(function temp() { let param1 = {url: 'http://www.google.com/'}; let param2 = {url: 'https://www.yahoo.com/'}; let data1Promise = function1(param1); let data2Promise = data1Promise.then(data1 => function2(data1, param2)); // ! Promise.all([data1Promise, data2Promise]).then(([data1, data2]) => { return function3(data1, data2); }).catch(error => { console.error("There was a problem:", error); }) })();
与async.waterfall
有什么不同?
waterfall
需要你写这些函数,以便他们用下一步需要的所有信息调用callback
函数。 stream程如下所示:
function1 -> (data1) function2 -> (data1, data2) function3
想象一下,如果你不得不连锁10个电话,而不是2个…基本上步骤2需要知道步骤3,4,5,6可能需要什么。
有了承诺,你可以通过从每个任务返回一个数组来做同样的事情,但是你可以做得更好:
不再需要用function1
和function2
来包装ogs
,因为你可以这样做:
Promise.all([ogs(...), ogs(...), ogs(...)]).then(allResults)
所有的东西都会收集在一个数组中。
非常相关的阅读: 当一个承诺依赖于另一个时,Bluebird的Promise.all()方法
但是我的API不会回复诺言!
我希望我现在有你的承诺,但你仍然坚持这个签名:
ogs(options, function (err, results) {...})
我们希望将其转换为如下forms:
ogsAsync(options) -> Promise
手动使用Promise构造函数很简单:
function ogsAsync(options) { return new Promise((resolve, reject) => { ogs(options, (err, results) => { if (err) { reject(err); } else { resolve(results); } }); }); }
但你可能不需要,因为看起来你的库已经返回一个promise ,所以你可以直接调用osg(options)
– 它已经返回一个promise 。 好极了!
但是,如果你不得不使用一个不提供承诺的库(比如redis
或者多个node
标准库), Bluebird提供了一个很好的工具来自动将callback风格的任务包装到承诺风格的任务中。
希望有所帮助!
所以我平时做的是设置一种“引导”function,作为我瀑布中的第一个function,将参数引入,并启动链条传递给他们。
function start(params){ params = params || {}; // make sure you have at least an empty object here return function(callback){ // do something callback(null, params); // error is always the first cb param for most things } } function second(params, callback){ // do something else. Maybe extend the params object params.newProp = "foo"; callback(null, params); } // later, maybe in another module async.waterfall([ start({foo : 'bar'}), second ], function result(e, res){ // handle result });
- AWS Elastic Beanstalk – 如何使用npm和webpack构buildbundle JS
- ngCordova FileTransfer直接上传到AWS S3 WebKitFormBoundary问题