ES6 Promise.all进展

时间:2017-02-20 09:53:45

标签: javascript es6-promise

在进一步讨论之前,我需要解决几项承诺。

Promise.all(promises).then((results) => {
  // going further
}); 

我有什么方法可以取得Promise.all承诺的进展吗?

从文档中可以看出it is not possible。并且this question也没有回答。

所以:

  • 你不同意这会有用吗?我们不应该查询此功能吗?
  • 现在如何手动实现它?

6 个答案:

答案 0 :(得分:24)

我已经敲了一个你可以重复使用的辅助功能。

基本上按照惯例传递你的承诺,并提供一个回调来做你想要的进展..



function allProgress(proms, progress_cb) {
  let d = 0;
  progress_cb(0);
  for (const p of proms) {
    p.then(()=> {    
      d ++;
      progress_cb( (d * 100) / proms.length );
    });
  }
  return Promise.all(proms);
}

function test(ms) {
  return new Promise((resolve) => {
    setTimeout(() => {
       console.log(`Waited ${ms}`);
       resolve();
     }, ms);
  });
}


allProgress([test(1000), test(3000), test(2000), test(3500)],
  (p) => {
     console.log(`% Done = ${p.toFixed(2)}`);
});




答案 1 :(得分:4)

您可以为每个承诺添加.then()以计算完成的数量。 类似的东西:



var count = 0;

var p1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 5000, 'boo');
}); 
var p2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 7000, 'yoo');
}); 
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'foo');
}); 

var promiseArray = [
  p1.then(function(val) {
    progress(++count); 
    return val 
  }), 
  p2.then(function(val) {
    progress(++count); 
    return val 
  }), 
  p3.then(function(val) {
    progress(++count); 
    return val 
  })
]

function progress(count) {
  console.log(count / promiseArray.length);
}

Promise.all(promiseArray).then(values => { 
  console.log(values);
});




答案 2 :(得分:1)

Keith's answer相比,它有一些优点:

  • 进度回调永远不会同步触发
  • 进度报告并没有打破链条
  • 输入可以是任何可迭代的,例如Promise.all()接受,而不仅仅是数组
  • 回调收到的是ProgressEvent而不是百分比,因此您可以访问已解决的整数的promise和总金额
Promise.progress = async function progress (iterable, onprogress) {
  await this.resolve()

  const promises = Array
    .from(iterable)
    .map(this.resolve, this)
  const { length } = promises
  let resolved = 0
  const values = promises.map(async promise => {
    const value = await promise
    const event = new ProgressEvent('progress', {
      total: length,
      loaded: ++resolved
    })

    await this.resolve(onprogress(event))

    return value
  })
  const event = new ProgressEvent('progress', {
    total: length,
    loaded: resolved
  })

  await this.resolve(onprogress(event))

  return this.all(values)
}

ProgressEvent has limited support显然不明显。如果这困扰您,您可以轻松添加:

class ProgressEvent extends Event {
  constructor (type, { loaded = 0, total = 0 } = {}) {
    super(type)
    this.loaded = loaded
    this.total = total
  }
}

答案 3 :(得分:0)

@Keith除了我的评论外,这是一个修改

(编辑以详细说明希望)

// original allProgress
//function allProgress(proms, progress_cb) {
//  let d = 0;
//  progress_cb(0);
//  proms.forEach((p) => {
//    p.then(()=> {    
//      d ++;
//      progress_cb( (d * 100) / proms.length );
//   });
//  });
//  return Promise.all(proms);
//}

//modifying allProgress to delay 'p.then' resolution
//function allProgress(proms, progress_cb) {
//     let d = 0;
//     progress_cb(0);
//     proms.forEach((p) => {
//       p.then(()=> {
//         setTimeout( //added line
//           () => {
//                 d ++;
//                 progress_cb( (d * 100) / proms.length );
//           },       //added coma :)
//           4000);   //added line
//       });
//     });
//     return Promise.all(proms
//            ).then(()=>{console.log("Promise.all completed");});
//            //added then to report Promise.all resolution
// }

//modified allProgress
// version 2 not to break any promise chain
function allProgress(proms, progress_cb) {
    let d = 0;
    progress_cb(0);
    proms.forEach((p) => {
      p.then((res)=> {                        //added 'res' for v2
        return new Promise((resolve) => {     //added line for v2
          setTimeout(   //added line
              () => {
                    d ++;
                    progress_cb( (d * 100) / proms.length );
                    resolve(res);             //added line for v2
              },        //added coma :)
            4000);      //added line
        });                                   //added line for v2
      });
    });
    return Promise.all(proms
                   ).then(()=>{console.log("Promise.all completed");});
                   //added then chaining to report Promise.all resolution
}


function test(ms) {
  return new Promise((resolve) => {
    setTimeout(() => {
       console.log(`Waited ${ms}`);
       resolve();
     }, ms);
  });
}


allProgress([test(1000), test(3000), test(2000), test(3500)],
  (p) => {
     console.log(`% Done = ${p.toFixed(2)}`);
});

“承诺全部完成”将在任何进度消息之前输出

这是我得到的输出

% Done = 0.00
Waited 1000
Waited 2000
Waited 3000
Waited 3500
Promise.all completed
% Done = 25.00
% Done = 50.00
% Done = 75.00
% Done = 100.00

答案 4 :(得分:0)

这是我的看法。您为progressCallback创建一个包装器,并告诉您有多少个线程。然后,对于每个线程,使用线程索引从该包装创建一个单独的回调。像以前一样,每个线程都通过自己的回调来报告,但是随后,它们各自的进度值将通过合并的回调进行合并和报告。

{,,foobar64d.dll}global::foo

答案 5 :(得分:0)

您可以将 my npm package 与原生承诺的扩展版本一起使用,该版本支持开箱即用的高级进度捕获,包括嵌套承诺Live sandbox

import { CPromise } from "c-promise2";

(async () => {
  const results = await CPromise.all([
    CPromise.delay(1000, 1),
    CPromise.delay(2000, 2),
    CPromise.delay(3000, 3),
    CPromise.delay(10000, 4)
  ]).progress((p) => {
    console.warn(`Progress: ${(p * 100).toFixed(1)}%`);
  });

  console.log(results); // [1, 2, 3, 4]
})();

或者有并发限制(Live sandbox):

import { CPromise } from "c-promise2";

(async () => {
  const results = await CPromise.all(
    [
      "filename1.txt",
      "filename2.txt",
      "filename3.txt",
      "filename4.txt",
      "filename5.txt",
      "filename6.txt",
      "filename7.txt"
    ],
    {
      async mapper(filename) {
        console.log(`load and push file [${filename}]`);
        // your async code here to upload a single file
        return CPromise.delay(1000, `operation result for [${filename}]`);
      },
      concurrency: 2
    }
  ).progress((p) => {
    console.warn(`Uploading: ${(p * 100).toFixed(1)}%`);
  });

  console.log(results);
})();