admin管理员组文章数量:1334927
Let's imagine that I have 10 promises running that will be passed to Promise.map()
with its default concurrency setting of 3
.
If any of the first 3 promises are rejected, will the other 7 be even started?
Bluebird's docs for map()
states the following:
Promises returned by the mapper function are awaited for and the returned promise doesn't fulfill until all mapped promises have fulfilled as well. If any promise in the array is rejected, or any promise returned by the mapper function is rejected, the returned promise is rejected as well.
It only states that if all fulfill, then the inner promises are awaited, but it's not clear to me what happens if any of them is rejected.
Edit
I'm aware that the canonical definition of the map
function is that the output's length is the same as the size of the input, but I'm not sure if Bluebird honors this.
Let's imagine that I have 10 promises running that will be passed to Promise.map()
with its default concurrency setting of 3
.
If any of the first 3 promises are rejected, will the other 7 be even started?
Bluebird's docs for map()
states the following:
Promises returned by the mapper function are awaited for and the returned promise doesn't fulfill until all mapped promises have fulfilled as well. If any promise in the array is rejected, or any promise returned by the mapper function is rejected, the returned promise is rejected as well.
It only states that if all fulfill, then the inner promises are awaited, but it's not clear to me what happens if any of them is rejected.
Edit
I'm aware that the canonical definition of the map
function is that the output's length is the same as the size of the input, but I'm not sure if Bluebird honors this.
- 1 can't you write a test and find out? – epascarello Commented Nov 15, 2016 at 20:52
- Yes, I am doing it right now, but I was hopping if there was something in a dark corner of the docs that addresses this question. – Henrique Barcelos Commented Nov 15, 2016 at 20:55
- 1 @epascarello There have been times where I've been able to test something, but that would take 20 minutes and I'm paring many different options so that's more time than I'd like to spend, so I appreciate questions like this. I hope Henrique will share with us if he does find the answer later. :) – Jeremy Banks Commented Nov 15, 2016 at 21:00
-
"If any of the first 3 promises are rejected, will the other 7 be even started?" I'm not sure what you mean by this. Promises cannot be "started". You can invoke a function which returns a promise, but Bluebird's
.map()
function does not take an array of functions to invoke. It takes an array of pre-existing promises and simply attaches a.then()
handler to them. So by the time you're calling Bluebird's.map()
function, you've already "started" all of those promises. – idbehold Commented Nov 15, 2016 at 23:29
2 Answers
Reset to default 7Taken literally, Bluebird's Promise.prototype.map
returns immediately.
When doing so, it returns a Promise
that may not resolve immediately. I imagine you really want to know how that promise behaves and there are a few things to break down here:
If any of the first 3 promises are rejected, will the other 7 be even started?
Yes. Promises are "started" (that is, scheduled) when you create them. The other 7 will attempt to resolve or are likely enough to do so that you need to assume they will.
Imagine if the browser only allows 4 HTTP connections to a server and you make 10 requests. Those first (failing) 3 will be sent, along with a friend, who may not fail but will certainly run.
You should assume that all promises will invoke their bodies.
It only states that if all fulfill, then the inner promises are awaited, but it's not clear to me what happens if any of them is rejected.
That's easy enough to test:
const Promise = require('bluebird');
function delayReject(delay, err) {
return new Promise((res, rej) => {
console.log('waiting to reject', err);
setTimeout(() => rej(err), delay);
});
}
function delayValue(delay, val) {
return new Promise((res, rej) => {
console.log('waiting to resolve', val);
setTimeout(() => res(val), delay);
});
}
const promises = [1, 2, 3, 4, 5, 6, 7, 8, 9].map(it => {
if (it % 3 === 0) {
return delayReject(50, it);
} else {
return delayValue(50, it);
}
});
Promise.map(promises, v => {
console.log('mapping', v);
return -v;
}).then(it => {
console.log('mapped', it);
}).catch(err => {
console.log('error', err);
});
My output, with node v6.8.1
, is:
ssube@localhost ~/questions/40619451 $ > node early-exit.js
waiting to resolve 1
waiting to resolve 2
waiting to reject 3
waiting to resolve 4
waiting to resolve 5
waiting to reject 6
waiting to resolve 7
waiting to resolve 8
waiting to reject 9
mapping 1
mapping 2
error 3
As you may expect, all the promises are scheduled and run, but the map
does stop running against them after the first failure.
The Bluebird docs mention that:
The mapper function for a given item is called as soon as possible, that is, when the promise for that item's index in the input array is fulfilled. This doesn't mean that the result array has items in random order, it means that
.map
can be used for concurrency coordination unlike.all
.
That suggests that the order of mapped items may not be persisted, like it is in the above example. We can test that by adding some noise to the delay:
const Promise = require('bluebird');
function delayNoise(n) {
return n + Math.floor(Math.random() * 50);
}
function delayReject(delay, err) {
return new Promise((res, rej) => {
console.log('waiting to reject', err);
setTimeout(() => rej(err), delayNoise(delay));
});
}
function delayValue(delay, val) {
return new Promise((res, rej) => {
console.log('waiting to resolve', val);
setTimeout(() => res(val), delayNoise(delay));
});
}
const promises = [1, 2, 3, 4, 5, 6, 7, 8, 9].map(it => {
if (it % 3 === 0) {
return delayReject(50, it);
} else {
return delayValue(50, it);
}
});
Promise.map(promises, v => {
console.log('mapping', v);
return -v;
}).then(it => {
console.log('mapped', it);
}).catch(err => {
console.log('error', err);
});
Running that yields far more interesting results: if the first promise rejects, then the map ends and does not attempt to map the others. It does short-circuit, as you guessed.
I have achieved very similar results to @ssube's answer.
I got 10 promises, that will be resolved or rejected after a increasing timeout. The 4th one (because the array is 0-based) is rejected.
const Promise = require('bluebird')
function delay(timeout, err, i) {
return new Promise(function (resolve, reject) {
if (err) {
setTimeout(function () {
console.log('Rejected', err.message)
reject(err)
}, timeout)
} else {
setTimeout(function () {
console.log('Resolved', i)
resolve(i)
}, timeout)
}
})
}
const promises = Array.apply(null, {length: 10})
.map(Function.call, Number)
.map(function (it) {
if (it === 3) {
return delay(500 * it, new Error(it))
} else {
return delay(500 * it, null, it)
}
})
Promise.map(promises, function (p) {
console.log('Mapping', p)
return p.toString()
})
.then(function (it) {
console.log('All resolved', it)
})
.catch(function (err) {
console.log('Error', err.message)
})
This will yield:
> Resolved 0
> Mapping 0
> Resolved 1
> Mapping 1
> Resolved 2
> Mapping 2
> Rejected 3
> Error 3
> Resolved 4
> Resolved 5
> Resolved 6
> Resolved 7
> Resolved 8
> Resolved 9
So, the behaviour is the following:
Promise.map
is short-circuited whenever one of the promises being mapped is rejected.- The callback in
map
is never executed for any of the subsequent promises. - Notice that
Error 3
es before the deliberately slower promises. - However, all of the promises are "executed" until they are settled.
本文标签: javascriptDoes Bluebird quotmapquot return earlyStack Overflow
版权声明:本文标题:javascript - Does Bluebird "map" return early? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742373600a2462718.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论