ES6 Promises - 类似 async.each 的东西?
Posted
技术标签:
【中文标题】ES6 Promises - 类似 async.each 的东西?【英文标题】:ES6 Promises - something like async.each? 【发布时间】:2015-11-08 19:13:43 【问题描述】:试图弄清楚如何找到功能与 async.eachSeries 完全相同的东西,我需要一个按顺序运行(而不是并行)的异步操作列表,但在本机 ES6 中找不到方法,可以有人建议吗?
附言考虑过生成器/产量,但还没有经验,所以我没有意识到它到底能对我有多大帮助。
编辑 1
每个请求,这里是一个例子:
假设这段代码:
let model1 = new MongooseModel(prop1: "a", prop2: "b");
let model2 = new MongooseModel(prop1: "c", prop2: "d");
let arr = [model1 , model2];
现在,我想以一系列而不是并行的方式运行它,因此使用“异步”NPM 很容易:
async.eachSeries(arr, (model, next)=>
model.save.then(next).catch(next);
, err=>
if(err) return reject(error);
resolve();
)
我的问题是:使用 ES6,我可以在本地实现吗?没有 NPM 'async' 包?
编辑 2
使用 async/await 可以轻松完成:
let model1 = new MongooseModel(prop1: "a", prop2: "b");
let model2 = new MongooseModel(prop1: "c", prop2: "d");
let arr = [model1 , model2];
for(let model of arr)
await model.save();
【问题讨论】:
你的意思是,第二个函数取决于第一个函数的结果? 不是必须依赖它,而是必须在前一个完成后运行。 您应该举一个例子并说明示例输入和预期输出,以正确解释问题。 加了个例子,谢谢! 【参考方案1】:您可以通过在then
回调中返回来进行链接。例如:
new Promise(function(resolve, reject)
resolve(1)
).then(function(v)
console.log(v);
return v + 1;
).then(function(v)
console.log(v)
);
将打印:
1 2
这在异步解析 Promise 时当然有效:
new Promise(function(resolve, reject)
setTimeout(function()
resolve(1);
, 1000)
).then(function(result)
return new Promise(function(resolve, reject)
setTimeout(function()
console.log(result);
resolve(result + 1);
, 1000)
);
).then(function(results)
console.log(results);
);
印刷:
1 2
【讨论】:
谢谢,但我需要它来动态收集数据,而不是一些已知的静态迭代量.. @ShlomiSasson 这只是一个例子。then
所做的只是返回一个承诺,你传递回调并根据需要创建承诺。
Kit,所以你说的是创建一个递归函数,将越来越多的回调附加到“then”方法?
@ShlomiSasson 好吧,您不必递归,就像 jfriend00 对 reduce 所做的那样。顺便说一句,有一个 ES7 提议让这个特殊情况更容易:github.com/lukehoban/ecmascript-asyncawait【参考方案2】:
假设您想对数据数组调用某个异步函数,并且希望它们按顺序调用,而不是并行调用。
async.eachSeries()
的界面是这样的:
eachSeries(arr, iterator, [callback])
下面是如何用 Promise 模拟它:
// define helper function that works kind of like async.eachSeries
function eachSeries(arr, iteratorFn)
return arr.reduce(function(p, item)
return p.then(function()
return iteratorFn(item);
);
, Promise.resolve());
这假定iteratorFn
将要处理的项目作为参数并返回一个承诺。
这是一个用法示例(假设您有一个已承诺的 fs.readFileAsync()
)并有一个名为 speak()
的函数,该函数在完成时返回一个承诺:
var files = ["hello.dat", "goodbye.dat", "genericgreeting.dat"];
eachSeries(files, function(file)
return fs.readFileAsync(file).then(function(data)
return speak(data);
);
);
这让 Promise 基础架构可以为您排序所有内容。
您也可以手动排序(虽然我不确定为什么):
function eachSeries(arr, iteratorFn)
return new Promise(resolve, reject)
var index = 0;
function next()
if (index < arr.length)
try
iteratorFn(arr[index++]).then(next, reject);
catch(e)
reject(e);
else
resolve();
// kick off first iteration
next();
);
或者,手动将 Promise 链接在一起的更简单的版本:
function eachSeries(arr, iteratorFn)
var index = 0;
function next()
if (index < arr.length)
return iteratorFn(arr[index++]).then(next);
return Promise.resolve().then(next);
请注意其中一个手动版本必须如何将iteratorFn()
与try/catch
括起来,以确保它是抛出安全的(将异常转换为拒绝)。 .then()
是自动抛出安全的,因此其他方案不必手动捕获异常,因为 .then()
已经为您捕获了它们。
【讨论】:
@Startec -.reduce()
接受两个参数。第一个是一个函数,第二个是你要减少的初始值。 Promise.resolve()
正在生成该初始值。然后使用两个参数调用您传递的回调。第一个参数是归约中的当前值。我在我的代码中将其命名为p
,它的初始值将是Promise.resolve()
。后续值将是我的回调 return p.then(...)
的返回值,这是另一个承诺。回调的第二个参数是我们要减少的数组中的下一个值。
@Startec - 有关.reduce()
的文档,请参阅here on MDN。你这样称呼它:var reducedValue = array.reduce(fn, initialValue)
。我的Promise.resolve()
是initialValue
。这只是一种通过将 Promise 链初始化为已解决的 Promise 来启动 Promise 链的方法。有点像Promise.resolve().then(fn1).then(fn2).then(fn3)
。
@Startec - 当您考虑到正确的错误处理和潜在的嵌套操作时,promise 无疑是管理事物的最佳方式。此外,它们是 ES7 中 async
和 await
的核心,因此它们也是语言的未来。如果我想对数组进行异步排序,我会在 Bluebird Promise 库中使用 Promise.mapSeries()
之类的东西,这是我通常用来添加扩展 Promise 功能的库。
@Startec - 也许您应该发布一个问题,并详细说明您正在尝试做什么,并询问使用 Promises 的最佳方法是什么。我无法从您使用的单词中看出您的实际问题,因此最好查看您的代码,然后人们可以提供更好/不同的方法来做到这一点。我从未发现使用 Promise 比不使用 Promise 需要更多代码的情况。与许多事情一样,编写 Promise 代码有好方法,但不是那么好。
@DaveCausey - 是的,第一种方法提前创建了整个迭代长度的初始承诺链,然后让它运行。其他方法一次链接一个新的 Promise,因为前一个 Promise 解决了,旧的 Promise 可以被 GCed。如果您的迭代时间非常长(例如数百万次),那么后一种选项可能会使用明显较少的峰值内存量。【参考方案3】:
对于喜欢简短回答的人:
[func1, func2].reduce((p, f) => p.then(f), Promise.resolve());
【讨论】:
第二个参数是减少的initial value。例如。这个特定的例子等价于Promise.resolve().then(func1).then(func2)
。
谢谢,当最后一个promise解决时,你会把代码放在哪里?
@SSHThis 将.then(resultFromFunc2 => console.log("all done"))
放在最后,;
之前。
很有趣,虽然我不确定这与Promise.all([func1, func2]);
有何不同。
@ChadJohnson 异步函数按顺序运行,而不是并行运行。【参考方案4】:
//为运行较低nodejs版本(Azure :/)的系统上传这个 不是最短但我能想到的最好的
例如,让我们说“functionWithPromise”返回一些承诺并期望一些项目。
functionWithPromise(item);
promisesArray =[];
//syncornized
itemsArray.forEach(function (item)
promisesArray.push(functionWithPromise(item));
);
Promise.all(promisesArray).then(function (values)
//profit
);
【讨论】:
这会并行运行 Promise,而不是按顺序运行【参考方案5】:作为answer provided by @jib...的扩展,您还可以将一组项目映射到异步函数,如下所示:
[item1, item2]
.map(item => async (prev_result) => await something_async(item))
.reduce((p, f) => p.then(f), Promise.resolve())
.then(() => console.log('all done'));
注意prev_result
将如何成为先前对something_async
的评估返回的值,这大致相当于async.eachSeries
和async.waterfall
之间的混合。
【讨论】:
以上是关于ES6 Promises - 类似 async.each 的东西?的主要内容,如果未能解决你的问题,请参考以下文章
Promisifying xml2js 解析函数(ES6 Promises)
[Node.js] Testing ES6 Promises in Node.js using Mocha and Chai