Advanced-Frontend/Daily-Interview-Question

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

zeroone001 opened this issue · 44 comments

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

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

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()方法将多个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状态。

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 就失败了. 对业务是不是没那么友好

一、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 commented
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 commented

说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))
      }
  })
}
  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); //不执行
    });

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

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实例

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

学习了

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

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

过分了昂

兄得长城是不是要哭倒了

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的结果

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

借鉴这两位兄台的代码: @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))
		}
	})
}

牛逼

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

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

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

过分了昂

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

说一下对处理异常方式的理解。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方法指定的回调函数。

说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了

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

有问题欢迎指正!

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

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

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)
        })
      }
    })
  }
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)不对吧

一、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

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

更多

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

继续留下眼泪,啊哈哈哈

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

        })

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