Skip to content

第 80 题:介绍下 Promise.all 使用、原理实现及错误处理 #130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
zeroone001 opened this issue May 23, 2019 · 44 comments
Open
Labels

Comments

@zeroone001
Copy link

zeroone001 commented May 23, 2019

const p = Promise.all([p1, p2, p3]);

Promise.all方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。(Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。)

@swchenxixi
Copy link

@irina9215
Copy link

all(list) {
        return new Promise((resolve, reject) => {
            let resValues = [];
            let counts = 0;
            for (let [i, p] of list) {
                resolve(p).then(res => {
                    counts++;
                    resValues[i] = res;
                    if (counts === list.length) {
                        resolve(resValues)
                    }
                }, err => {
                    reject(err)
                })
            }
        })
    }

@Vera1128
Copy link

今天又是流下没有技术的眼泪的一天?

@swchenxixi
Copy link

今天又是流下没有技术的眼泪的一天?

过分了昂

@513107329
Copy link

Promise.all()方法将多个Promise实例包装成一个Promise对象(p),接受一个数组(p1,p2,p3)作为参数,数组中不一定需要都是Promise对象,但是一定具有Iterator接口,如果不是的话,就会调用Promise.resolve将其转化为Promise对象之后再进行处理。
使用Promise.all()生成的Promise对象(p)的状态是由数组中的Promise对象(p1,p2,p3)决定的;
1、如果所有的Promise对象(p1,p2,p3)都变成fullfilled状态的话,生成的Promise对象(p)也会变成fullfilled状态,p1,p2,p3三个Promise对象产生的结果会组成一个数组返回给传递给p的回调函数;
2、如果p1,p2,p3中有一个Promise对象变为rejected状态的话,p也会变成rejected状态,第一个被rejected的对象的返回值会传递给p的回调函数。
Promise.all()方法生成的Promise对象也会有一个catch方法来捕获错误处理,但是如果数组中的Promise对象变成rejected状态时,并且这个对象还定义了catch的方法,那么rejected的对象会执行自己的catch方法,并且返回一个状态为fullfilled的Promise对象,Promise.all()生成的对象会接受这个Promise对象,不会返回rejected状态。

@lvtraveler
Copy link

参考:使用Promise.all

@cnvoid
Copy link

cnvoid commented May 27, 2019

all(list) {
        return new Promise((resolve, reject) => {
            let resValues = [];
            let counts = 0;
            for (let [i, p] of list) {
                resolve(p).then(res => {
                    counts++;
                    resValues[i] = res;
                    if (counts === list.length) {
                        resolve(resValues)
                    }
                }, err => {
                    reject(err)
                })
            }
        })
    }

这么写的话是只要又一个promise失败, 整个.all 就失败了. 对业务是不是没那么友好

@luohong123
Copy link

luohong123 commented May 27, 2019

一、Promise概念

Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一。Promise.all()接受一个由promise任务组成的数组,可以同时处理多个promise任务,当所有的任务都执行完成时,Promise.all()返回resolve,但当有一个失败(reject),则返回失败的信息,即使其他promise执行成功,也会返回失败。和后台的事务类似。和rxjs中的forkJoin方法类似,合并多个 Observable 对象 ,等到所有的 Observable 都完成后,才一次性返回值。

二、Promise.all如何使用

对于 Promise.all(arr) 来说,在参数数组中所有元素都变为决定态后,然后才返回新的 promise。

// 以下 demo,请求两个 url,当两个异步请求返还结果后,再请求第三个 url
const p1 = request(`http://some.url.1`)
const p2 = request(`http://some.url.2`)
Promise.all([p1, p2])
  .then((datas) => { // 此处 datas 为调用 p1, p2 后的结果的数组
    return request(`http://some.url.3?a=${datas[0]}&b=${datas[1]}`)
  })
  .then((data) => {
    console.log(msg)
  })

三、Promise.all原理实现

function promiseAll(promises){
     return new Promise(function(resolve,reject){
            if(!Array.isArray(promises)){
             return reject(new TypeError("argument must be anarray"))
           }
    var countNum=0;
    var promiseNum=promises.length;
    var resolvedvalue=new Array(promiseNum);
    for(var i=0;i<promiseNum;i++){
      (function(i){
         Promise.resolve(promises[i]).then(function(value){
            countNum++;
           resolvedvalue[i]=value;
          if(countNum===promiseNum){
              return resolve(resolvedvalue)
          }
       },function(reason){
        return reject(reason)
      )
     })(i)
    }
})
}
var p1=Promise.resolve(1),
p2=Promise.resolve(2),
p3=Promise.resolve(3);
promiseAll([p1,p2,p3]).then(function(value){
console.log(value)
})

四、Promise.all错误处理

有时候我们使用Promise.all()执行很多个网络请求,可能有一个请求出错,但我们并不希望其他的网络请求也返回reject,要错都错,这样显然是不合理的。如何做才能做到promise.all中即使一个promise程序reject,promise.all依然能把其他数据正确返回呢?

1、全部改为串行调用(失去了node 并发优势)

2、当promise捕获到error 的时候,代码吃掉这个异常,返回resolve,约定特殊格式表示这个调用成功了

var p1 =new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve(1);
    },0)
});
var p2 = new Promise(function(resolve,reject){
        setTimeout(function(){
            resolve(2);
        },200)
 });
 var p3 = new Promise(function(resolve,reject){
        setTimeout(function(){
            try{
            console.log(XX.BBB);
            }
            catch(exp){
                resolve("error");
            }
        },100)
});
Promise.all([p1, p2, p3]).then(function (results) {
    console.log("success")
     console.log(results);
}).catch(function(r){
    console.log("err");
    console.log(r);
});

@lhyt
Copy link

lhyt commented May 29, 2019

Promise.all = function (promiseArrs) { //在Promise类上添加一个all方法,接受一个传进来的promise数组
    return new Promise((resolve, reject) => { //返回一个新的Promise
        let arr = []; //定义一个空数组存放结果
        let i = 0;
        function handleData(index, data) { //处理数据函数
            arr[index] = data;
            i++;
            if (i === promiseArrs.length) { //当i等于传递的数组的长度时 
                resolve(arr); //执行resolve,并将结果放入
            }
        }
        for (let i = 0; i < promiseArrs.length; i++) { //循环遍历数组
            promiseArrs[i].then((data) => {
                handleData(i, data); //将结果和索引传入handleData函数
            }, reject)
        }
    })
}

@lhyt
Copy link

lhyt commented May 29, 2019

说all体验不好,那我们也可以自己做一个some方法,表示全部失败才算失败

Promise.some = function (promiseArrs) {
  return new Promise((resolve, reject) => {
      let arr = []; //定义一个空数组存放结果
      let i = 0;
      function handleErr(index, err) { //处理错误函数
          arr[index] = err;
          i++;
          if (i === promiseArrs.length) { //当i等于传递的数组的长度时 
            reject(err); //执行reject,并将结果放入
          }
      }
      for (let i = 0; i < promiseArrs.length; i++) { //循环遍历数组
          promiseArrs[i].then(resolve, (e) => handleErr(i, e))
      }
  })
}

@jackieli123723
Copy link

  var p11 = Promise.resolve(3).catch(function(err) {
      return err;
    });
    var p22 = Promise.reject(2).catch(function(err) {
      return err;
    });
    var p33 = new Promise((resolve, reject) => {
      setTimeout(resolve, 100, "foo");
    }).catch(function(err) {
      return err;
    }); 
    
    Promise.all([p11, p22, p33]).then(values => { 
      console.log(values); // [3, 2, "foo"]
    }).catch(function(err) {
      console.log(err); //不执行
    });

@zhoushoujian
Copy link

借助楼上的代码,可以看出promise.a
ll虽然可以称为并发,依然是单线程的,和后端的并发实质性不一样.js的多线程并发可以借助cluster,各个子进程取到的结果统一返回给主进程进行管理,父子进程通讯机制与react父子组件通讯相似。

@zhuli2010
Copy link

all(list) {
        return new Promise((resolve, reject) => {
            let resValues = [];
            let counts = 0;
            for (let [i, p] of list) {
                resolve(p).then(res => {
                    counts++;
                    resValues[i] = res;
                    if (counts === list.length) {
                        resolve(resValues)
                    }
                }, err => {
                    reject(err)
                })
            }
        })
    }

promise里面,形参resolve只执行一次的,你这里应该调用promise.resolve(p)来获取promise实例

@cheungzh
Copy link

cheungzh commented Aug 1, 2019

function promiseAll(promises = []) {
  let result = [];
  function check(resolve) {
    let length = result.length;
    if (length === promises.length) {
      resolve(result);
    }
  }
  return new Promise(resolve => {
    for (let  i = 0; i < promises.length; i++) {
      let promise = promises[i];
      promise.then(res => {
        result[i] = res;
        check(resolve);
      });
    }
  })
}

let promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('定时器1')
  }, 3000)
});

let promise2 = new Promise(resolve => {
  setTimeout(() => {
    resolve('定时器2')
  }, 2000);
})

promiseAll([promise2, promise1]).then(res => {
  console.log(res)
})

@corlme
Copy link

corlme commented Aug 3, 2019

学习了

@monan19920722
Copy link

今天又是流下没有技术的眼泪的一天?

今天又是流下没有技术的眼泪的一天?

过分了昂

兄得长城是不是要哭倒了

@gyf940349398
Copy link

all(list) {
        return new Promise((resolve, reject) => {
            let resValues = [];
            let counts = 0;
            for (let [i, p] of list) {
                resolve(p).then(res => {
                    counts++;
                    resValues[i] = res;
                    if (counts === list.length) {
                        resolve(resValues)
                    }
                }, err => {
                    reject(err)
                })
            }
        })
    }

resolve(p)这里有问题,因为p已经是一个Promise实例了,不需要resolve,状态发生改变(无论成功或失败)会自动进入then回调;如果此处resolve,会导致始终只能拿到一个promise的结果

@canmick
Copy link

canmick commented Sep 25, 2019

function promiseAll(arr = []) {
        return new Promise((resolve, reject) => {
          const result = [],
            len = arr.length;
          arr.forEach(item => {
            Promise.resolve(item).then(
              res => {
                result.push(res);
                if (result.length === len) {
                  resolve(result);
                }
              },
              err => {
                reject(err);
              }
            );
          });
        });
      }

@hugeorange
Copy link

hugeorange commented Sep 25, 2019

借鉴这两位兄台的代码: @irina9215 @luohong123

  • Promise.all
Promise.all1 = function(list) {
	return new Promise((resolve, reject) => {
		let count = 0
		let resArr = []
		for (let i=0; i<list.length; i++) {
			// Promise.resolve list 内部的非promise对象元素转成 promise 对象
		        let p = list[i].then ? list[i] : Promise.resolve(list[i])
			p.then(res => {
				count++
				resArr[i] = res
				if (count === list.length) {
					resolve(resArr)
				}
			}).catch(err => {
				reject(err)
			})
		}
	})
}
  • Promise.all 异常处理
// 原理:
// 当某一个请求失败时走进 reject 或者被 catch 捕获到了
// 可以创建一个新的 promise 手动调用 Promise.resolve(errMsg)
// 这样 Promise.all 就认为数组里的promise全部执行成功了

// 两种处理方法:
// 一种直接在每个子项Promise做catch处理并将错误结果return出来
// 另外一种对list做map处理为其每项假如 catch
var p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('p1执行了...')
        resolve('p1')
    }, 1000)
})
// .catch(err => err)  此处直接 return err 相当于在链条上新 resolve(err),会在接下来的链条中体现

var p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('p2执行了...')
        resolve('p2')
    }, 2000)
})
// .catch(err => err)

var p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('p3执行了...')
        reject('p3失败了,我是错误信息')
    }, 100)
})
// .catch(err => err)

let list = [p1, p2, p3]
Promise.all(list.map(v => {
    let p = v.then ? v : Promise.resolve(v) // 对不是promise的子项包装成promise
    return p.catch(err => err)
}))
.then(res => console.log(res))

  • 另外又简单的写了下 Promise.race 的实现
Promise.race1 = function(list) {
	return new Promise((resolve, reject) => {
		for (let i=0; i<list.length; i++) {
			let p = list[i].then ? list[i] : Promise.resolve(list[i])
			p.then(res => resolve(res))
				.catch(err => reject(err))
		}
	})
}

@Stephen526
Copy link

牛逼

@liyikun
Copy link

liyikun commented Oct 13, 2019

Promise._all = function(list) {
let resValue = []
let count = 0

return new Promise((resolve, reject) => {
    list.forEach((p, i) => {
        p.then((res) => {
            count++
            resValue[i] = res
            if(count === list.length) {
                resolve(resValue)
            }
        }, err => {
            reject(err)
        })
    })
})

}

var a = new Promise((resolve) => {
setTimeout(() => {
resolve(1000)
}, 1000)
})

var b = new Promise((resolve) => {
setTimeout(() => {
resolve(2000)
}, 1000)
})

Promise.all1([a, b]).then((c) => {
console.log(c)
})

@yygmind yygmind added the 异步 label Dec 16, 2019
@bbrucechen
Copy link

bbrucechen commented Dec 26, 2019

Promise.all()的参数是传入一个数组,数组的值是Promise对象,这个函数返回一个Promise对象
这个函数顾名思义就是检查传入的所有数组是否都执行成功,如果都成功那么这个函数返回的Promise对象进入resolve状态并将所有promise成功的参数组成一个数组传给resolve函数,如果其中任何一个失败,那么就进入reject状态,并将第一个reject的promise的参数传给Promise.all返回的promise对象的reject函数

原理实现:

function promiseAll(promiseArr) {
  return new Promise((resolve,reject) => {
    let count = 0;
    const result = []
    for(let i = 0;i < promiseArr.length;i++){
      promiseArr[i].then(res => {
        count ++
        result.push(res)
        if(count == promiseArr.length) {
          resolve(result)
        }
      },rej => reject(rej))
    }
  })
}

const pro1 = new Promise((res,rej) => {
  setTimeout(() => {
    res('1')
  },1000)
})
const pro2 = new Promise((res,rej) => {
  setTimeout(() => {
    res('2')
  },2000)
})
const pro3 = new Promise((res,rej) => {
  setTimeout(() => {
    res('3')
  },3000)
})

const proAll = promiseAll([pro1,pro2,pro3]).then(res => console.log(res)).catch((e) => {
  console.log(e)
})

@ManyDai
Copy link

ManyDai commented Jan 7, 2020

今天又是流下没有技术的眼泪的一天?

过分了昂

流下没有技术眼泪的第218天

@ztfstar
Copy link

ztfstar commented Jan 7, 2020

说一下对处理异常方式的理解。const p = Promise.all([p1,p2])的特点是当参数数组中的promise实例p1、p2都返回resolved时p才会返回resloved,只要参数数组实例中有一个返回rejected时,p就会返回reject,另一个resolved的结果也没有被返回,所以这并不是我们想要的结果。怎么处理这种异常呢?
其实给数组中的promise实例定义了错误处理catch方法的时候,就不会在走p的catch的方法,且参数实例在执行完catch方法之后状态会变成resolved。

var p1 =new Promise(function(resolve,reject){
	resolve(1);
})
.then(result => result)
.catch(e => e);

var p2 = new Promise(function(resolve,reject){
	resolve(2);
})
.then(result => result)
.catch(e => e);

Promise.all([p1, p2])
.then(function (result) {
	console.log(results);
})
.catch(e={
	console.log(e);
});

执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。

@skychenbo
Copy link

说all体验不好,那我们也可以自己做一个some方法,表示全部失败才算失败

Promise.some = function (promiseArrs) {
  return new Promise((resolve, reject) => {
      let arr = []; //定义一个空数组存放结果
      let i = 0;
      function handleErr(index, err) { //处理错误函数
          arr[index] = err;
          i++;
          if (i === promiseArrs.length) { //当i等于传递的数组的长度时 
            reject(err); //执行reject,并将结果放入
          }
      }
      for (let i = 0; i < promiseArrs.length; i++) { //循环遍历数组
          promiseArrs[i].then(resolve, (e) => handleErr(i, e))
      }
  })
}

这段代码岂不是没有起到all的作用,只要有一个resolve, 就不会执行后面的promise了

@SerinaJingyiLu
Copy link

function promiseAll(promises){
     return new Promise(function(resolve,reject){
            if(!Array.isArray(promises)){
             return reject(new TypeError("argument must be anarray"))
           }
    var countNum=0;
    var promiseNum=promises.length;
    var resolvedvalue=new Array(promiseNum);
    for(let i=0;i<promiseNum;i++){      
         Promise.resolve(promises[i]).then(function(value){          
           countNum += 1;
           resolvedvalue[i] = value;
           if(countNum === promiseNum){
           	resolve(resolvedvalue);
           }
           }).catch(function(value){
           	resolve(value);
         })
    }
})
}
let p1 = Promise.resolve(1);
let p2 = Promise.resolve(2);
let p3 = Promise.resolve(3);
let p4 = Promise.reject(4);
promiseAll([p1,p2,p3]).then(function(value){
console.log(value)
})
promiseAll([p1,p2,p4]).then(function(value){
console.log(value)
})

两个结果分别是:
Screen Shot 2020-03-06 at 3 24 56 AM

有问题欢迎指正!

@lz-lee
Copy link

lz-lee commented Jun 1, 2020

all(list) {
        return new Promise((resolve, reject) => {
            let resValues = [];
            let counts = 0;
            for (let [i, p] of list) {
                resolve(p).then(res => {
                    counts++;
                    resValues[i] = res;
                    if (counts === list.length) {
                        resolve(resValues)
                    }
                }, err => {
                    reject(err)
                })
            }
        })
    }

resolve(p)这里有问题,因为p已经是一个Promise实例了,不需要resolve,状态发生改变(无论成功或失败)会自动进入then回调;如果此处resolve,会导致始终只能拿到一个promise的结果

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
Promise.resolve(value)方法返回一个以给定值解析后的Promise 对象。如果这个值是一个 promise ,那么将返回这个 promise ; 他写的问题在没用 Promise.resolve

@yanyue404
Copy link

yanyue404 commented Jul 11, 2020

Promise.all 实现:

var promise1 = 41;
var promise2 = 42;
var promise3 = new Promise(function (resolve, reject) {
  setTimeout(resolve, 5000, 'foo');
});

function p1(time) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve(time);
    }, time);
  });
}

// Promise 扩展
Promise.all__fake = (promiseAry) => {
  return new Promise((resolve, reject) => {
    let resultAry = [],
      index = 0;
    for (let i = 0; i < promiseAry.length; i++) {
      Promise.resolve(promiseAry[i])
        .then((result) => {
          index++;
          resultAry[i] = result;
          if (index === promiseAry.length) {
            resolve(resultAry);
          }
        })
        .catch((reason) => {
          reject(reason);
        });
    }
  });
};

Promise.all__fake([promise1, promise2, promise3]).then(function (values) {
  console.log(values);
});
Promise.all__fake([p1(5000), p1(1000)]).then(function (res) {
  console.log(res); //[5000,1000]
});

Promise.all 错误处理:

使用 try catch,使 promise 不进入 reject 函数

const getVideoList = () => {
  return new Promise((resolve, reject) => {
    API.getHomeVideo({ agtId: this.data.agtId }).then((res) => {
      try {
        let result = res.data.data.slic(0, 6); // slice 书写错误
        resolve({ swiperList: result });
      } catch (error) {
        resolve([]);
      }
    });
  });
};

Promise.every 扩展,所有的 promise 都错误才触发 reject,否则一律返回

Promise.every = (promiseAry) => {
  // 所有的 promise 都错误才触发 reject
  return new Promise((resolve, reject) => {
    let resultAry = [],
      errorAry = [],
      index = 0,
      index__error = 0;
    for (let i = 0; i < promiseAry.length; i++) {
      Promise.resolve(promiseAry[i])
        .then((result) => {
          index++;
          resultAry[i] = result;
          if (index === promiseAry.length) {
            resolve(resultAry);
          }
        })
        .catch((reason) => {
          index__error++;
          errorAry[i] = reason;
          // 都有都错误
          if (index__error === promiseAry.length) {
            reject(errorAry);
          }
          if (index + index__error === promiseAry.length) {
            resolve(resultAry);
          }
        });
    }
  });
};

@slogeor
Copy link

slogeor commented Jul 12, 2020

Promise.all 使用

场景1: p1、p3 是 Promise 实例;p2 是普通变量

p1 = new Promise((resolve, reject) => {
    resolve('p1');
})

p2 = 'p2';

p3 = new Promise((resolve, reject) => {
    resolve('p3');
})

p = Promise.all([p1, p2, p3]);
p.then((data) => {
    console.log('data:', data);
}).catch((error) => {
    console.log('error:', error);
})

// 输出结果: data: (3) ["p1", "p2", "p3"]

场景2:p1、p2 resolve;p3 reject,没有单独进行catch处理

p1 = new Promise((resolve, reject) => {
    resolve('p1');
})

p2 = new Promise((resolve, reject) => {
    resolve('p2');
})

p3 = new Promise((resolve, reject) => {
    reject('p3');
})

p = Promise.all([p1, p2, p3]);
p.then((data) => {
    console.log('data:', data);
}).catch((error) => {
    console.log('error:', error);
})
// 输出结果:error: p3

场景3: p1、p2 resolve;p3 reject,catch 有处理

p1 = new Promise((resolve, reject) => {
    resolve('p1');
})

p2 = new Promise((resolve, reject) => {
    resolve('p2');
})

p3 = new Promise((resolve, reject) => {
    reject('p3 error');
}).catch(() => {
   return Promise.resolve('p3')
})

p = Promise.all([p1, p2, p3]);
p.then((data) => {
    console.log('data:', data);
}).catch((error) => {
    console.log('error:', error);
})
// 输出结果:data: (3) ["p1", "p2", "p3"]

Promise.all 方法接受一个数组作为参数(可以不是数组,但必须具有 Iterator 接口),p1、p2、p3 都是 Promise 实例(如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例)。

Promise.all 原理实现
Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let index = 0;
        let result = [];
        if (promises.length === 0) {
            resolve(result);
        } else {
            function processValue(i, data) {
                result[i] = data;
                if (++index === promises.length) {
                    resolve(result);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                // promises[i] 可能是普通值
                Promise.resolve(promises[i]).then((data) => {
                    processValue(i, data);
                }, (err) => {
                    reject(err);
                    return;
                });
            }
        }
    });
}
Promise.all 原理错误处理

p1、p2、p3 都 resolve,会触发 p 的 then 方法,参数 data 是数组,每一项取值对应该 Promise 实例 resolve 的值。

当 p1、p2、p3 没有实现自己 catch 方法时, 其中一个 reject,会触发 p 的 catch 方法,参数 error 是参数列表中第一个 reject 出来的值。

当 p1、p2、p3 有实现自己 catch 方法时, 其中一个 reject,触发 p 的 then 方法还是 catch 方法,取决于 Promise 实例 reject 后对应的 catch 方法是如何处理的。

@tjwyz
Copy link

tjwyz commented Jul 16, 2020

	static all(arr) {
		return new PromiseA((resolve, reject)=>{
			let i = 0;
			let ret = [];
			while(arr.length) {
				let tmp = arr.shift();
				tmp.then((param)=>{
					ret[i] = param;
					i++
					if (i == arr.length) {
						resolve(ret);
					}
				},(err) => {
					reject(err);
				})
			}
		});
	}

@m7yue
Copy link

m7yue commented Sep 10, 2020

all 是 Promise 的静态方法

  static all (list) {
    return new MyPromise((resolve, reject) => {
      let values = []
      let count = 0
      for (let [i, p] of list.entries()) {
        // 数组参数如果不是 Promise 实例,先调用 Promise.resolve, 静态方法 this指向类
        this.resolve(p).then(res => {
          values[i] = res
          count++
          // 所有状态都变成fulfilled时返回的 Promise状态就变成fulfilled
          if (count === list.length) resolve(values)
        }, err => {
          // 有一个被rejected时返回的 Promise状态就变成rejected
          reject(err)
        })
      }
    })
  }

@ningyunhan
Copy link

all(list) {
        return new Promise((resolve, reject) => {
            let resValues = [];
            let counts = 0;
            for (let [i, p] of list) {
                resolve(p).then(res => {
                    counts++;
                    resValues[i] = res;
                    if (counts === list.length) {
                        resolve(resValues)
                    }
                }, err => {
                    reject(err)
                })
            }
        })
    }

你这里直接resolve(p)不对吧

@MrLeihe
Copy link

MrLeihe commented May 9, 2021

一、Promise概念

Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一。Promise.all()接受一个由promise任务组成的数组,可以同时处理多个promise任务,当所有的任务都执行完成时,Promise.all()返回resolve,但当有一个失败(reject),则返回失败的信息,即使其他promise执行成功,也会返回失败。和后台的事务类似。和rxjs中的forkJoin方法类似,合并多个 Observable 对象 ,等到所有的 Observable 都完成后,才一次性返回值。

二、Promise.all如何使用

对于 Promise.all(arr) 来说,在参数数组中所有元素都变为决定态后,然后才返回新的 promise。

// 以下 demo,请求两个 url,当两个异步请求返还结果后,再请求第三个 url
const p1 = request(`http://some.url.1`)
const p2 = request(`http://some.url.2`)
Promise.all([p1, p2])
  .then((datas) => { // 此处 datas 为调用 p1, p2 后的结果的数组
    return request(`http://some.url.3?a=${datas[0]}&b=${datas[1]}`)
  })
  .then((data) => {
    console.log(msg)
  })

三、Promise.all原理实现

function promiseAll(promises){
     return new Promise(function(resolve,reject){
            if(!Array.isArray(promises)){
             return reject(new TypeError("argument must be anarray"))
           }
    var countNum=0;
    var promiseNum=promises.length;
    var resolvedvalue=new Array(promiseNum);
    for(var i=0;i<promiseNum;i++){
      (function(i){
         Promise.resolve(promises[i]).then(function(value){
            countNum++;
           resolvedvalue[i]=value;
          if(countNum===promiseNum){
              return resolve(resolvedvalue)
          }
       },function(reason){
        return reject(reason)
      )
     })(i)
    }
})
}
var p1=Promise.resolve(1),
p2=Promise.resolve(2),
p3=Promise.resolve(3);
promiseAll([p1,p2,p3]).then(function(value){
console.log(value)
})

四、Promise.all错误处理

有时候我们使用Promise.all()执行很多个网络请求,可能有一个请求出错,但我们并不希望其他的网络请求也返回reject,要错都错,这样显然是不合理的。如何做才能做到promise.all中即使一个promise程序reject,promise.all依然能把其他数据正确返回呢?

1、全部改为串行调用(失去了node 并发优势)

2、当promise捕获到error 的时候,代码吃掉这个异常,返回resolve,约定特殊格式表示这个调用成功了

var p1 =new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve(1);
    },0)
});
var p2 = new Promise(function(resolve,reject){
        setTimeout(function(){
            resolve(2);
        },200)
 });
 var p3 = new Promise(function(resolve,reject){
        setTimeout(function(){
            try{
            console.log(XX.BBB);
            }
            catch(exp){
                resolve("error");
            }
        },100)
});
Promise.all([p1, p2, p3]).then(function (results) {
    console.log("success")
     console.log(results);
}).catch(function(r){
    console.log("err");
    console.log(r);
});

如果无论成功或失败都执行 resolve,可以用 Promise.allSettled

@jackluson
Copy link

Tip: 1. Promise.all() 方法接收一个 promise 的 iterable 类型(注:Array,Map,Set 都属于 ES6 的 iterable 类型)的输入,并且只返回一个 Promise 实例, 那个输入的所有 promise 的 resolve 回调的结果是一个数组。这个 Promise 的 resolve 回调执行是在所有输入的 promise 的 resolve 回调都结束,或者输入的 iterable 里没有 promise 了的时候。它的 reject 回调执行是,只要任何一个输入的 promise 的 reject 回调执行或者输入不合法的 promise 就会立即抛出错误,并且 reject 的是第一个抛出的错误信息。
2. 输出结果按照 promise 顺序输出(很多人都忽略这点)
3. 数组如果不是promise实例,要转化成promise实例 (很多人都忽略这点)

Promise.myAll = function (promises) {
  return new Promise((resolve, reject) => {
    let promiseRes = [];
    promises.forEach(async (pr, index) => {
      if (!(pr instanceof Promise)) {
        pr = pr;
      }
      try {
        const temp = await pr;
        promiseRes.splice(index, 0, temp);
        // promiseRes.push(temp);
        if (promiseRes.length === promises.length) {
          resolve(promiseRes);
        }
      } catch (error) {
        reject(error);
      }
    });
  });
};

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "foo");
});
// const pErr = new Promise((resolve, reject) => {
//   reject("总是失败");
// });

Promise.myAll([promise1, promise2, promise3])
  .then((values) => {
    console.log(values);
  })
  .catch((err) => {
    console.log("err", err);
  });

更多

@PastelSubliminal
Copy link

Promise.all(values),返回一个 promise 实例。如果迭代器中所有的 promise 参数状态都是 resolved, 则 promise 实例的状态为 resolved,其 PromiseValue 为每个参数的 PromiseValue 组成的数组;
如果参数中的 promise 有一个失败(rejected),此实例的状态为 rejected,其 PromiseValue 为是第一个失败 promise 的 PromiseValue

Promise.all = function(values) {
  return new Promise((resolve, reject) => {
      var result = []
      var resolvedCount = 0
      if (value.length == 0) {
        resolve([])
        return
      }
      for (let i = 0; i < values.length; i++) {
        Promise.resolve(values[i]).then(val => {
            result[i] = val
            resolvedCount++
            if (resolvedCount == values.length) {
              resolve(result)
            }
        } reason => {
            reject(reason)
        })
      }
  })
}

@Vera1128
Copy link

Vera1128 commented Feb 22, 2022 via email

@Four-Names
Copy link

let Promise_all = async (Promise_Arr = new Array()) => {
  let results = [],
    errors = null;

  for (let i = 0; i < Promise_Arr.length; i++) {
    if (Promise_Arr[i] instanceof Promise) {
      Promise_Arr[i]
        .then((v) => {
          results.push(v);
        })
        .catch((err) => {
          errors = err;
        });
    } else {
      throw new Error("检测到非Promise的存在");
    }
  }
  return new Promise(async (resolve, reject) => {
    while (1) {
      await new Promise((resolve) => setTimeout(resolve, 100));
      if (errors != null) reject(errors);
      if (results.length == Promise_Arr.length) {
        resolve(results);
        break;
      }
    }
  });
};

Promise_all([
  Promise.resolve(1),
  Promise.resolve(2),
  new Promise(async (resolve, reject) => {
    await new Promise((resolve) => setTimeout(resolve, 3000));
    resolve(3);
  }),
  Promise.reject(4),
])
  .then((res) => {
    console.log("Promise_all", res);
  })
  .catch((err) => {
    console.log("Promise_all", err);
  });

Promise.all([
  Promise.resolve(1),
  Promise.resolve(2),
  new Promise(async (resolve, reject) => {
    await new Promise((resolve) => setTimeout(resolve, 3000));
    resolve(3);
  }),
  Promise.reject(4),
])
  .then((res) => {
    console.log("Promise.all", res);
  })
  .catch((err) => {
    console.log("Promise.all", err);
  });

@Lingdeqing
Copy link

Promise._all = function (promises) {
    return new Promise((resolve, reject) => {
        const result = [], resolved = 0
        const length = promises.length
        const addResult = (item, index) => {
            result[index] = item
            resolved++
            if (resolved === length) {
                resolve(result)
            }
        }
        promises.forEach((promise, index) => {
            if (typeof promise.then === 'function') {
                // thenable
                promise.then((item) => {
                    addResult(item, index)
                }).catch(reject)
            } else {
                // 非promise类型直接透传
                addResult(promise, index)
            }
        })
    })
}

@Vera1128
Copy link

Vera1128 commented Mar 19, 2022 via email

@Yangfan2016
Copy link

Yangfan2016 commented Aug 23, 2022

Promise.all = function (fns) {
    const res = [];
    return new Promise((rs, rj) => {
        fns.forEach((fn, n) => {
            Promise.resolve(fn).then(r => {
                res[n] = r;  // 结果按顺序插入
                if (n === res.length - 1) rs(res);
            }, e => {
                rj(e);
            })
        });
    })
}

@xinghunMeng
Copy link

继续留下眼泪,啊哈哈哈

@lastertd
Copy link

lastertd commented Sep 6, 2023

    static all<T extends Iterable<any>>(args: T) {      //约定T从属于Iterable类型
        return new MyPromise((resolve, reject) => {
            const promise = Array.from(args);    //将可迭代对象转化为数组
            let index: number = 0;
            let result: any[] = [];
            const foo = function (pos: number, data: any) {
                result[pos] = data;
                // 所有的promise对象都为fulfilled则改变状态
                if (++index === promise.length) {
                    resolve(result);
                }
            }
            if (promise.length === 0) {     //如果为空则直接改状态为fulfilled
                resolve(result);
            } else {
                for (let i = 0; i < promise.length; i++) {
                    // 使用MyPromise包裹一层,将原始值转化为MyPromise
                    MyPromise.resolve(promise[i]).then(data => {
                        foo(i, data);
                    }, reason => {
                        reject(reason);
                    })
                }
            }

        })

    }

@Dylan0916
Copy link

Promise.myAll = (promises) => {
  const _promises = Array.isArray(_promises) ? _promises : [promises];
  const result = [];
  let fulfilledNum = 0;

  return new Promise((resolve, reject) => {
    if (_promises.length === 0) return resolve([])

    _promises.forEach((promise, index) => {
      Promise.resolve(promise).then((resp) => {
        result[index] = resp;
        fulfilledNum += 1;

        if (result.length === _promises.length) {
          resolve(result);
        }
      }, reject);
    });
  });
};

@negativeentropy9
Copy link

Promise.fakeAll = function (promises) {
  return new Promise((resolve, reject) => {
    const result = [];
    let count = 0;

    promises.forEach((p, i) => {
      p.then(
        (data) => {
          count++;
          result[i] = data;

          if (count === promises.length) {
            resolve(result);
          }
        },
        (e) => reject(e)
      );
    });
  });
};

function createPromise(i) {
  return Promise.resolve(i);
}

console.log(
  Promise.fakeAll([createPromise(1), createPromise(2)]).then((data) => {
    console.log(data);
  })
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests