从for循环填充变量时如何使用promise

How to use promise when variable populated from for loop

我有一个函数可以进行多次异步调用,这些调用使用返回的数据填充同一个对象。一旦对象完全填充,我需要对数据做一些事情,并且由于有多个调用,这不是基本的 callback/promise 场景。

在这种情况下是否可以创建一个承诺?简化代码:

price_options = [] // when this is populated from all the async calls, I need to do stuff with it
sheet_columns = [3,5,7,89]

useServiceAccountAuth(credentials, function(error){ //google docs api

  for (var i = 0; i < sheet_columns.length; i++) {
    var params = {column_number: sheet_cols[i]}

    do_async_call(params, function (e, data) {
      data.forEach( function(item) {
        price_options.push(item)
      })
    })
  }
})

您需要创建承诺数组。 您可以在此处了解更多信息。

http://bluebirdjs.com/docs/api/promise.all.html

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

由于其他小伙伴已经说明了Promise.all的用法,所以我用Promise.all写了这个片段给大家看一下。

price_options = [];
sheet_columns = [3,5,7,89];
var promises = [];

useServiceAccountAuth(credentials, function(error){ //google docs api

  for (var i = 0; i < sheet_columns.length; i++) {
    var params = {column_number: sheet_cols[i]}

    // create a new promise and push it to promises array
    promises.push(new Promise(function(resolve, reject) {
     do_async_call(params, function (e, data) {
       resolve(data);
     });
    }));
  }

  // now use Promise.all
  Promise.all(promises).then(function (args) {
    args.forEach(function (data, i) {
       data.forEach(function(item) {
        price_options.push(item)
       });
    });
    // here do your stuff which you want to do with price_options
  });
})

你可以这样做:

let results = sheet_columns.map(c => ({column_number: c}))
    .map(params => new Promise((resolve, reject) => {

    do_async_call(params, (e, data) => {
        if(e) {
            reject(e);
        } else {
            resolve(data);
        }
    })
}))

Promise.all(results).then(arr => Array.prototype.concat.apply([], arr)).then(price_options => doSomething(price_options))

Working jsbin here

如果您想使用 promise,请将您的 do_async_call 函数包装在一个 promise 返回函数中。

price_options = [];
sheet_columns = [3,5,7,89]

useServiceAccountAuth(credentials, function(error){ //google docs api

    var promise_array = [];
    for (var i = 0; i < sheet_columns.length; i++){
        var params = {column_number: sheet_cols[i]}
        var promise = do_async_promise(params);
        promise_array.push(promise);
    }
    Q.all(promise_array).then(function(){

    //do your operation with price_options here;
  });

})

function do_async_promise(params){
    var deferred = Q.defer();
    do_async_call(params, function (e, data) {
      data.forEach( function(item) {
        price_options.push(item);
      });
       deferred.resolve();
    })
    return deferred.promise;
}

其他答案对他们来说有很多错误信息。

您应该做的是使用 Promise.all() 聚合所有 Promise。 Promise.all() 采用一个 Promise 数组,returns 一个 Promise,当数组中的所有 Promise 都已解析时解析。

所以现在,您需要创建一个函数来获取每个 params 条目,并为其上的数据创建一个 Promise,并将其推送到一个新数组中。

由于我们使用的是 Promises,所以让我们去掉代码中的所有其他回调:

// The "functionNameAsync" convention indicates that the function returns Promises.
// This convention was coined by Bluebird's promisifying functions.

// Takes credentials
// Returns a promise that rejects on error, or resolves with nothing on no error.
const useServiceAccountAuthAsync = credentials => 
  new Promise((resolve, reject) =>
    useServiceAccountAuth(credentials, err => err ? reject(err) : resolve()));

const doCallAsync = params => 
  new Promise((resolve, reject) =>
    do_async_call(params, (err, data) => err ? reject(err) : resolve(data)));

/* If you opt to use Bluebird, everything above this line can be replaced with:
const useServiceAccountAuthAsync = Promise.promisify(useServiceAcountAuth);
const doCallAsync = Promise.promisify(do_async_call);

it would even be faster than my version above. */

// Now time for the actual data flow:

const sheet_columns = [3,5,7,89]

useServiceAccountAsync()
  .then(() => {
     const arrayOfAsyncCallPromises = sheet_columns
    .map(columnNumber => ({column_number: sheet_cols[columnNumber]}))
    .map(doCallAsync);
  //.map(param => doCallAsync(param)) equivalent to above

     return Promise.all(arrayOfAsyncCallPromises);
  })
  .then(price_options => {
    // use here
  })
  .catch(err => {
    // handle errors here
  });