promise.all是如何工作的?

13

我开始研究Promise,发现了有趣的Promise.all。

在MDN上它被定义为:

Promise.all(iterable)方法返回一个promise,当可迭代参数中所有promise都已解决时,该promise将解决。

这基本上意味着,如果参数列表中的所有promise都已解决,那么设定的promise会在它们之后解决。我尝试了实现它,并进行了简单的promise ajax调用。

var get = function(url) {
    return new Promise(function(resolve,reject) {
        var xhtml=new XMLHttpRequest();

        xhtml.open("GET",url);
        xhtml.responseType = 'blob';
        xhtml.onload = function() {
            if(xhtml.status==200){
                resolve(xhtml.response);
            } else {
                reject(Error("Error"+statusText));
            }
        }
        xhtml.send();
    });

}

get("one.jpg").then(function(response){
    var blob = window.URL.createObjectURL(response);
    var img = document.createElement("img");

    console.log("Success"+response);

    img.src = blob;

    document.body.appendChild(img);
});

这个代码本来是正常的。但是当我尝试添加Promise.all之后,它就抛出了一个错误。

Promise.all(get).then(function(response){alert("done")});

就像我所说的,出现了一个错误:"Promise.all的第1个参数无法转换为序列。" 所以我认为我没有理解Promise.all的含义。它是如何工作的?


4
get 是一个函数;Promise.all() 接收一个 Promise 对象数组。 - Dan
4个回答

8

Promise.all 接受一个由 Promise 组成的数组(或任何可迭代对象),当它们全部成功时就会成功,当其中一个失败时就会失败。如果我们实现一下并了解为什么需要它,那么理解起来会更容易。

一个常见的用例可能是等待窗口加载完成并且服务器返回数据以便运行一些代码:

// a function that returns a promise for when the document is ready.
function windowReady(){
    return new Promise(function(resolve){
         window.addEventListener('DOMContentLoaded', resolve);
    }); 
}

// function that returns a promise for some data
function getData(){
    return fetch("/").then(function(r){ return r.json() });
}

现在,我们希望它们同时执行并获得结果。这里有两个项目,但也可能有5个或100个要等待的项目。因此,我们使用Promise.all

Promise.all([windowReady(), getData()]).then(function(results){
     // results[1] is the data, it's all in an array.
});

让我们看看如何实现它:
function all(iterable){ // take an iterable 
  // `all` returns a promise.  
  return new Promise(function(resolve, reject){ 
    let counter = 0; // start with 0 things to wait for
    let results = [], i = 0;
    for(let p of iterable){
        let current = i;
        counter++; // increase the counter 
        Promise.resolve(p).then(function(res){ // treat p as a promise, when it is ready: 
          results[i] = res; // keep the current result
          if(counter === 0) resolve(results) // we're done
        }, reject); // we reject on ANY error
       i++; // progress counter for results array
    }
  });
}

或者,更加符合ES6的写法:

let all = iterable => new Promise((resolve, reject) => { 
  let arr = [...iterable], c = arr.length, results = [];
  arr.map(Promise.resolve, Promise).
      map((p, i) => p.then(v => { 
        r[i] = v;
        if(--c === 0) resolve(r);
      } , reject));
});

有趣的解释,谢谢!但我不确定我是否理解了其中的一些内容。1)在这里,Promise.resolve(p).then(function(res)“将p视为一个promise”,我假设非promise会被传递到这里。这意味着then回调中的参数将返回p,但如果我们在那里传递promise会发生什么?then会传递什么?还有2)在windowReady中,您将resolve作为一个没有参数的函数传递给eventListener,我不确定您想要传达什么。感谢您的回答! - Darlyn
有人能解释一下你的示例代码中这行代码的含义吗:let results = [], i; 当我在控制台测试时,'results' 被赋值为空数组,而 'i' 变成了未定义。当你尝试在未定义的变量上使用 ++ 运算符时,它会变成 NaN。这是怎么回事? - jhaubrich.com
@jhaubrich.com 这个代码已经有6年了,所以你的猜测和我的一样好,但可能只是缺少了一个 i = 0 - Benjamin Gruenbaum
@BenjaminGruenbaum 所以你的意思是作者本意是将 i 初始化为 0,但在这个例子中不小心将其初始化为未定义? - jhaubrich.com
谢谢,这正是我所想的,但只是想确认一下,我没有漏掉什么。 - jhaubrich.com
显示剩余2条评论

4

你的get函数返回一个Promise。你只是传递了get函数的引用。你必须传递一个Promises数组。

Promise.all([get("one.jpg")]).then(...);

2

简而言之:

Promise.all 是一个Javascript方法,它使用可迭代对象(例如Array)的promise作为参数,并在可迭代对象中所有 promise 都已解决时(或者可迭代对象中不包含 promise 时)返回单个 promise。 它会以解析值的数组形式解析并拒绝第一个拒绝的 promise 的单个值。

示例:

var promise1 = Promise.resolve(5);
var promise2 = Math.random() > 0.5? 1 : Promise.reject(1); // either resolves or rejects
var promise3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('foo'), 1000);
});

Promise.all([promise1, promise2, promise3]).then((val) => {
  console.log(val);
}).catch((val) => {
  console.log(val);
});

在上面的例子中,3个Promise作为一个数组传递到了Promise.all函数中。Promise 1和3总是会被解决。Promise 2基于随机数生成器要么被解决要么被拒绝。然后,这个Promise.all方法会返回一个基于随机数生成器的已解决或已拒绝的Promise。
接着,then()方法和catch()方法可以在从Promise.all返回的Promise上调用。在本例中,then()方法得到所有已解决值的数组[5, 1, 'foo']。而catch()方法得到第一个被拒绝的Promise的值,在这个例子中是1
何时使用:
当你想要执行多个异步操作并在异步操作后对结果进行处理时,这个方法非常有用。当使用Promise.all时,所有的Promise都可以同时被处理,同时还能操作所有传入的数据。
例如,当我们需要使用多个AJAX请求获取信息并将数据组合成有用的东西时,等待所有数据可用就变得至关重要,否则我们将尝试组合不存在的数据,这将导致问题。

0
Promise.all(iterables) 函数返回一个单一的 Promise。在这里我们将多个 Promises 作为参数提供。只有当所有 Promises(参数)都被解决时,Promise.all(iterables) 函数才会返回 Promise。它会在第一个 Promise 参数被拒绝时拒绝。
var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values);
// expected output: Array [3, 42, "foo"]
});

语法:

Promise.all(func1, func2 [,funcN])

参数:

阅读更多 - https://www.oodlestechnologies.com/blogs/An-Introduction-To-Promise.all-Function

免责声明:我为创意科技工作


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接