蓝鸟"map"return早吗?

Does Bluebird "map" return early?

假设我有 10 个承诺 运行 将被传递给 Promise.map(),其默认并发设置为 3

如果前 3 个承诺中的任何一个被拒绝,其他 7 个是否会开始?

Bluebird's docs 对于 map() 声明如下:

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.

它只是说如果所有的都满足,那么内在的承诺就会被等待,但我不清楚如果其中任何一个被拒绝会发生什么。

编辑

我知道 map 函数的规范定义是输出的长度与输入的大小相同,但我不确定 Bluebird 是否支持这一点。

从字面上看,Bluebird的Promise.prototype.mapreturns马上.

这样做时,returns Promise 可能 不会 立即解决。我想您真的很想知道 that promise 的行为方式,这里有几件事需要分解:

If any of the first 3 promises are rejected, will the other 7 be even started?

是的。 Promise 在您创建时是 "started"(即已计划)。其他 7 个将尝试解决或可能足以解决问题,因此您需要假设他们会这样做。

想象一下,如果浏览器只允许与服务器建立 4 个 HTTP 连接,而您发出 10 个请求。那些前(失败)3 将与朋友一起发送,他可能不会失败但肯定会 运行。

你应该假设所有的 promise 都会调用它们的主体。

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.

这很容易测试:

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);    
});                        

我的输出 node v6.8.1 是:

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 

如您所料,所有承诺都已安排 运行,但 map 确实会在第一次失败后停止 运行ning。

Bluebird 文档提到:

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.

这表明映射项的顺序可能不会像上面的示例那样保持不变。我们可以通过在延迟中添加一些噪声来测试它:

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);                            
});                                  

运行 会产生更有趣的结果:如果第一个 promise 被拒绝,则映射结束并且不会尝试映射其他承诺。如您所料,它确实会短路。

我得到的结果与@ssube 的回答非常相似。

我得到了 10 个承诺,它们将在超时增加后得到解决或拒绝。第 4 个(因为数组是从 0 开始的)被拒绝。

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)
  })

这将产生:

> 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

因此,行为如下:

  • Promise.map 每当被映射的承诺之一被拒绝时就会短路。
  • map 中的回调永远不会为任何后续承诺执行。
  • 请注意 Error 3 出现在故意较慢的承诺之前。
  • 然而,所有的承诺都是"executed",直到它们得到解决。