我需要使用async.js模块执行异步功能。 但是当我执行外部函数时,我遇到了一些问题。
代码很顺利。
但是当我将全局变量更改为局部变量时,我无法使用参数进行设置。
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更改为局部变量,就像这样..
(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”我无法更改本地的功能类型
async.waterfall([
function(callback){
},
function(data,callback){
}],
function(err){
if(err) console.log(err);
}
);
答案 0 :(得分:2)
吱吱!我正在使用一些ES6语法,请至少在节点6上运行代码片段,好吗?
可以将异步任务建模为接受回调的函数:
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));
虽然两种惯例都有意义,但我已经看到第二种方法在实践中更有用,可以编写简单的异步代码并具有良好的错误处理能力。
最重要的要点是:
关于promises的重要一点是,与回调不同,它们保证是异步的:
// 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
这使得代码更易于推理,但这也意味着当您实际依赖第二种行为时,承诺将没有用处。
好的,让我们回到你的代码。首先让我用虚拟异步函数替换ogs
,这样我们就可以在没有网络的情况下使用一些代码:
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);
}
);
})();
返回承诺而不是回调的等效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
现在返回一个承诺,所以在每个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);
}
如果您想在中间添加一些日志记录,那也很容易:
function function2() {
return ogs(param2).then(data2 => {
console.log("inside function2", data2);
return data2;
});
}
现在每个步骤都是一个承诺返回异步任务,让我们将它们连接在一起!最简单的方法是直接使用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
。
并行运行网络请求应该可以让您的应用程序以更快的速度运行。赢了!
现在回到原来的问题:
我们可以完全控制function1
和function2
的签名,让我们使用它!让这些函数将param作为参数,而不是查看全局变量。像这样:
function function1(param){
return ogs(param);
}
function function2(param) {
return ogs(param, {"some other options": true});
}
这些功能现在看起来非常相似!也许你可以只使用一个(或者只是放弃它们并直接呼叫ogs
)。
删除全局变量后,我们的代码现在看起来像这样:
(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);
})
})();
如果没有function2
的结果,function1
实际上无法启动怎么办?
function function1(param) {
return ogs(param);
}
function function2(data1, param) {
return ogs(param2, {"some other options": data1});
}
我们可以使用嵌套then
恢复到第一个版本,但我们也可以尝试更整洁的内容:
(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
。流程如下:
function1
-> (data1)
function2
-> (data1, data2)
function3
想象一下,如果你必须链接10个电话而不是2个......基本上第2步需要知道第3,4,5,6步可能需要什么。
使用promises,你可以通过从每个任务返回一个数组来做同样的事情,但你可以做得更好:
不再需要使用ogs
和function1
包裹function2
,因为您可以这样做:
Promise.all([ogs(...), ogs(...), ogs(...)]).then(allResults)
并且所有内容都以数组形式收集。
非常相关的阅读:Bluebird's Promise.all() method when one promise is dependent on another
我希望我现在有你的承诺,但你仍然坚持这个签名:
ogs(options, function (err, results) {...})
我们希望将其转换为:
ogsAsync(options) -> Promise
使用Promise构造函数手动操作非常简单:
function ogsAsync(options) {
return new Promise((resolve, reject) => {
ogs(options, (err, results) => {
if (err) {
reject(err);
} else {
resolve(results);
}
});
});
}
但您可能不需要,因为looks like your library already returns a promise,所以您可以直接致电osg(options)
- 它已经返回承诺。 耶!
但是,如果您必须使用尚未提供承诺的库(如redis
或大多数node
标准库),Bluebird提供了一个很好的实用工具自动将回调式任务包装成承诺式任务。
希望有所帮助!
答案 1 :(得分:0)
所以我通常做的是设置一种' bootstrap'作为我瀑布中的第一个,它接受了参数并开始向前传递它们。
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
});