1.为什么promise不能在forEach中执行,只能在for循环中执行呢?
async function test() {
//这样不行
arr.forEach(item => {
await promiseFunc(item)
})
//这样可以
for (var i = 0; i < arr.length; i++) {
await promiseFunc(arr[i])
}
}
2.promise.map处理很大的数组(比如10000个元素)会导致内存占用很大吗?
async function test() {
//arr有10000个元素,这样是不是会占用10000个单位的内存呢?
await Promise.map(arr, promiseFunc)
//arr有10000个元素,加上 {concurrency: 5}参数,这样可不可以把内存占用降到5个单位呢?
await Promise.map(arr, promiseFunc, {concurrency: 5})
}
for的时候将异步动作丢到数组里,等到for完成后,用promise的方法处理数组啊
重复异步操作,建议用EventProxy.
ep.after("afterFiles",files.length,function( list ){
ep.emit("success",list);
});
files.forEach(function( value,index,arr ){
var temp = path + "\\" + value;
fs.stat(temp,ep.group('afterFiles',function( stat ){
if( stat.isFile() ) { //是文件
return { filename:temp,type:1 }; //1代表文件
} else if( stat.isDirectory() ) { //是文件夹
return { filename:temp,type:2 }; //2代表文件
}
}));
});
这是group的源码
EventProxy.prototype.group = function (eventname, callback) {
var that = this;
var group = eventname + '_group';
var index = that._after[group].index;
that._after[group].index++;
return function (err, data) {
if (err) {
// put all arguments to the error handler
return that.emit.apply(that, ['error'].concat(SLICE.call(arguments)));
}
that.emit(group, {
index: index,
// callback(err, args1, args2, ...)
result: callback ? callback.apply(null, SLICE.call(arguments, 1)) : data
});
};
};```
只是做计数,在大循环中肯定比对象快。
arr.forEach(async function(item){
await promiseFunc(item)
})
其实这种情况还是用Promise.all吧,你这种写会一个接一个地发送请求,很慢的
第一个问题:为什么不行? 因为 await 只能在有 async 的函数中运行,匿名函数没有 async
,所以不行。
如果想并发的执行,可以这样写:
arr.forEach(async function(item){
await promiseFunc(item)
})
如果你不想并发执行,想顺序执行,就使用你写的 for循环就可以了。
这个部分你可以参考阮一峰老师的这篇文章,我也是从他那里学到的。
第二个问题:要看语义是什么,在 js 标准里面我没有找到 Promise.map MDN,那我只好假定你使用的是 bluebird。
BuildBird中的map方法中的参数可以指定并发数,这样一次就可以执行多个promise。
但是,你问的是参数省内存,我认为参数已经占用了内存没有办法省了。
于是我猜想你的问题是:计算过程中省内存
。
更具体地表达是:在指定{concurrency: 5}
参数后,是否能省计算中的内存?
从这个问题来看,你不大确定,指定了concurrency
后,函数的运算情况:第一种可能: 在运算过程是否是一次性计算所有的值,然后等;第二种可能:还是一次计算5次,等5次计算完成后,再计算5次,一直这样计算完成。
参考 bluebird 的文档 promise.map,我倾向认为是第二种:当设置了并发值后,每次只有部分值参与计算,这样相当于省了函数计算的内存。
所以第二个问题的答案是:省内存,最多只有5个单位
。
@i5ting 桑老师说的是并发执行吧,我这里用for和forEach是想试试串行执行的效果。
@p2227 是的,所以我改并发,于是就有了第2个问题。
@htoooth 多谢! 第一个问题:确实是这个道理。 第二个问题:我是用的bluebird。按你讲的,这两种写法,参数(10000个参数 / 10000个promise对象)和结果(10000个结果,我这个promiseFunc是有返回值的)占用的内存是一样的,只是在计算时能省一点内存。如果是这样的话,那么第二种写法并不会产生省内存的明显效果了。
@zouzhenxing 例子木有看懂哦,碰到apply、call我就蒙了。感觉用事件还是没有promise直观啊。
@postgetme 下面这段是摘至 EventProxy Readme.md
重复异步协作
此处以读取目录下的所有文件为例,在异步操作中,我们需要在所有异步调用结束后,执行某些操作。
var ep = new EventProxy();
ep.after('got_file', files.length, function (list) {
// 在所有文件的异步执行结束后将被执行
// 所有文件的内容都存在list数组中
});
for (var i = 0; i < files.length; i++) {
fs.readFile(files[i], 'utf-8', function (err, content) {
// 触发结果事件
ep.emit('got_file', content);
});
}
after
方法适合重复的操作,比如读取10个文件,调用5次数据库等。将handler注册到N次相同事件的触发上。达到指定的触发数,handler将会被调用执行,每次触发的数据,将会按触发顺序,存为数组作为参数传入。
@zouzhenxing 明白了。用”回调+事件“还是用promise还是看个人喜好。现在不是流行用promise嘛。
你这个代码改成promise的写法是:
function readFileAsync(file) {
return new Promise((resolve, reject) => {
fs.readFile(file, 'utf-8', (err, content) => {
if (err) {
reject(err)
} else {
resolve(content)
}
})
})
}
Promise.all(files.map(readFileAsync)).then(list => {
})
@postgetme 明白的,我只是说一下我的思路。 我没有看过Promise.all的源码,如果是那个循环很大的情况下,也就是文件很多的情况下。 EventProxy只是进行了计数,没有新建对象,我个人觉得应该会快一些。 呵呵请指教
@zouzhenxing promise的原理我一点也不懂哦,效率应该是没有EventProxy高。