【问题标题】:How can I wait for the loop to Complete?如何等待循环完成?
【发布时间】:2020-11-10 23:14:48
【问题描述】:

让我先给你看看代码是什么样子的

 Cart.getCompleteCart((cart)=>{
    
       let products = []; *//this Array has to be filled*

       totalPrice = cart.totalPrice;

       for(let prod of cart.products){

                Product.getProductDetails(prod.productId, productDetails => {
                    products.push({
                        product: productDetails,
                        productCount: prod.productCount
                    });
                });

      }
      console.log("Products are ->", products); *//this line is running before for loop!*
 }
       

console.log() 在 for 循环完成之前运行

如何以与 console.log() 同步的方式运行“for 循环”?

【问题讨论】:

标签: javascript node.js loops asynchronous async-await


【解决方案1】:

我假设Product.getProductDetails() 是一个异步方法(它看起来像是对 API 的查询)。

console.log() 不是“在for 循环之前运行”,只是Product.getProductDetails() 执行的任务正在异步处理。

由于Product.getProductDetails() 方法似乎也适用于回调,因此实现目标的一种方法是承诺每个调用,然后使用Promise.all() 方法将所有承诺浓缩成一个。

这样的事情应该可以解决问题:

Cart.getCompleteCart((cart) => {
    const promises = [];

    for (let prod of cart.products) {
        promises.push(
            new Promise((resolve) => {
                Product.getProductDetails(prod.productId, (productDetails) => {
                    resolve({
                        product: productDetails,
                        productCount: prod.productCount
                    });
                });
            })
        );
    }

    Promise.all(promises).then((products) => {
        console.log('Products are ->', products);
    });
});

或者:

Cart.getCompleteCart((cart) => {
    Promise.all(
        cart.products.map((prod) => {
            return new Promise((resolve) => {
                Product.getProductDetails(prod.productId, (productDetails) => {
                    resolve({
                        product: productDetails,
                        productCount: prod.productCount
                    });
                });
            });
        })
    ).then((products) => {
        console.log('Products are ->', products);
    });
});

【讨论】:

  • 不要使用products.push,只需使用该对象解决承诺。 Promise.all 将实现预期的结果。
  • @Bergi 你是对的,我知道。我只是不想过多地更改代码以使其更具说教性。我会相应地更新我的答案。
【解决方案2】:

Promise.all 几乎就是为这个确切的用例而设计的:

// A dummy "Product" with a dummy "getProductDetails" implementation
// so that we can have a working test example
let Product = {
  getProductDetails: (productId, callback) => {
    setTimeout(() => {
      callback({ type: 'productDetails', productId });
    }, 100 + Math.floor(Math.random() * 200));
  }
};

// This is the function you're looking for:
let getCompleteCart = async (cart) => {
  
  return Promise.all(cart.products.map(async ({ productId, productCount }) => ({
    product: await new Promise(resolve => Product.getProductDetails(productId, resolve)),
    productCount
  })));
  
}

let exampleCart = {
  products: [
    { productId: 982, productCount: 1 },
    { productId: 237, productCount: 2 },
    { productId: 647, productCount: 5 }
  ]
};
getCompleteCart(exampleCart).then(console.log);

getCompleteCart的细分:

let getCompleteCart = async (cart) => {
  
  return Promise.all(cart.products.map(async ({ productId, productCount }) => ({
    product: await new Promise(resolve => Product.getProductDetails(productId, resolve)),
    productCount
  })));
  
}

Promise.all (mdn) 专门用于等待 Promise 数组中的每个 Promise 解决

我们需要向Promise.all 提供一个承诺数组。这意味着我们需要将我们的数据数组 (cart.products) 转换为 Promise 数组; Array.protoype.map 是完美的工具。

提供给cart.products.map 的函数将购物车中的每个产品转换为一个类似于{ product: <product details>, productCount: <###> } 的对象。

这里的棘手之处在于获取“product”属性的值,因为该值是异步的(由回调返回)。以下行创建了一个 Promise,它使用 Promise 构造函数 (mdn) 解析为 Product.getProductDetails 返回的值:

new Promise(resolve => Product.getProductDetails(productId, resolve))

await 关键字允许我们将此承诺转换为它产生的未来值。我们可以将这个未来值分配给“产品”属性,而“产品计数”属性只是从原始项目中复制而来购物车:

{
  product: await new Promise(resolve => Product.getProductDetails(productId, resolve)),
  productCount
}

为了在正确的时间运行console.log,我们需要等待所有产品详细信息完成编译。幸运的是,这是由getCompleteCart 在内部处理的。我们可以在适当的时候console.log等待getCompleteCart解决。

有两种方法可以做到这一点:

使用Promise.prototype.then (mdn):

getCompleteCart(exampleCart).then(console.log);

或者,如果我们在 async 上下文中,我们可以使用 await

let results = await getCompleteCart(exampleCart);
console.log(results);

【讨论】:

    猜你喜欢
    • 2021-04-23
    • 2012-09-30
    • 2017-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-19
    • 2021-07-14
    • 2015-06-24
    相关资源
    最近更新 更多