如何按照请求的顺序将异步响应推送到数组中?

时间:2018-02-01 08:37:18

标签: javascript arrays asynchronous foreach

我正在使用Web3.js查询各种以太坊智能合约的价格。这是异步完成的,很多时候这些值不按顺序返回,这意味着它们会以错误的顺序被推送到新数组。我想将值推送到forEach循环内的新数组,以便我可以使用jQuery更新页面上的值。保持秩序的最佳方法是什么?

采取的步骤:

首先,我将所有合同按顺序拼接成一个数组。

var Contracts = [];

Contract.splice(0, 0, EthereumContract.at("0x2a50fb3396455717043ae5aa631d362c94fe13e1"));
Contract.splice(1, 0, EthereumContract.at("0x69ec0658ff334789c7f57edc3a2f28adef1c0ef3"));
Contract.splice(2, 0, EthereumContract.at("0x4d2bab147c32c75c8c9277182e64c69e14cb9f3c"));
Contract.splice(3, 0, EthereumContract.at("0xcd56f2c89128ad71cfd87b0fc78c9e26990b0f66"));

然后我创建一个新的数组'TheCurrentPrice',它应该收到每份合约的价格。

var TheCurrentPrice = [];

现在我按顺序遍历数组并将Price方法应用于数组中的契约。我将返回的结果推送到一个新数组中。不幸的是,它有时会无序响应并被错误地推入新阵列。

Contract.forEach(function (Contract) {   
Contract.Price(function (error, result) {

    TheCurrentPrice.push(result);

    });
});

解决方案:

Alnitak给出了正确答案,但语法不正确,以防将来有人试图这样做。

function PricePromise(Contract) {
return new Promise((resolve, reject) => {

     Contract.Price(function (error, result) {
        if (error) {
            reject(error);
            console.log("Price Error");
        } else {

            resolve(result);
            console.log("Price " + result);

        }
         });
     });
};

Promise.all(Contract.map(PricePromise))
.then(function(Price) {

('#Product-Price-1').attr('data-price', Price[0] / 1000000000000000000);

/*This uses jQuery to update a custom data-* attribute in HTML5. Price[0] is of course the first entry in the array of prices retrieved with the promise.*/

...

});

我不太明白为什么resolve(result)允许我放弃Price方法的forEach循环,但是你不需要forEach,这是正确的语法。

3 个答案:

答案 0 :(得分:1)

.forEach方法包含一个索引,因此您可以使用它来填充TheCurrentPrice

Contracts.forEach(function (Contract, index) {   
    Contract.Price(function (error, result) {
        TheCurrentPrice[index] = result;
    });
});

但请注意,您仍然需要某种形式的同步来告诉您何时获得所有价格。

因此,更好的解决方案可能是将Price的调用封装为"Promises",然后使用Promise.all()等待所有这些调用解析。

function PricePromise(contract) {
    return new Promise(function(resolve, reject) {
        contract.Price(function(error, result) {
            if (error) {
                reject(error);
            } else {
                resolve(result);
            }
        });
    });
}

[注意:诸如bluebird.js之类的承诺库包括自动" Promisify"与Web3.js Price函数签名]

匹配的函数
Promise.all(Contracts.map(PricePromise)).then(function(TheCurrentPrice) {
    // continue processing here with the values
    // now already populated in "TheCurrentPrice"
    ...
});

如果您最终使用bluebird.js,可以稍微进一步简化上述内容:

Promise.map(Contracts, PricePromise).then(function(TheCurrentPrice) {
    ...
});

其中Promise.map函数(尽管它不是标准函数,但是由Bluebird添加的函数)包含的附加功能允许您限制将要放置的并发调用数。

答案 1 :(得分:0)

您可以使用Array.prototype.forEach(索引)的第二个参数:

var date = new Date();
var dateMoment = moment(date).format("YYYY-MM-DD hh:mm");
console.log(dateMoment);

答案 2 :(得分:0)

您可以尝试在forEach的回调中添加一个函数'index'参数,然后您可以将当前价格设置到正确的位置。

    Contract.forEach(function (Contract, index) {
        Contract.Price(function (error, result) {
            TheCurrentPrice[index]=result;
        });
    });