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

Javascript 如何按请求的顺序将异步响应推送到数组中?,javascript,arrays,asynchronous,foreach,Javascript,Arrays,Asynchronous,Foreach,我正在用Web3.js查询各种以太坊智能合约的价格。这是异步完成的,并且很多时候返回的值顺序不正确,这意味着它们以错误的顺序被推送到新数组。我希望将值推送到forEach循环中的新数组中,以便使用jQuery更新页面上的值。保持事物有序的最好方法是什么 采取的步骤: 首先,我将所有契约按顺序拼接到一个数组中 var Contracts = []; Contract.splice(0, 0, EthereumContract.at("0x2a50fb3396455717043ae5aa631d3

我正在用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"));
然后我创建一个新数组“当前价格”,它应该接收每个合同的价格

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.*/

...

});

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

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

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

        TheCurrentPrice[index] = result;

    });
});
.forEach方法包含一个索引,因此您可以使用该索引填充当前价格:

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

因此,更好的解决方案可能是将调用包装为Price,然后使用等待所有调用都得到解决

function PricePromise(contract) {
    return new Promise(function(resolve, reject) {
        contract.Price(function(error, result) {
            if (error) {
                reject(error);
            } else {
                resolve(result);
            }
        });
    });
}
[NB:Promise库,如include函数,用于自动提示与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添加的一个函数包含了允许您限制并发调用数量的附加功能。

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

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

用增量ID标记请求,并推送到数组的该索引。arr[id]=我在询问之前遇到的“Promise.all:Order of resolved values”线程的结果可能重复。这显然是答案的一部分,但Alnitak对特定用例的回答是正确的。另一个线程更为抽象,答案主要是需要ES6之后最新JS知识的行话,这是三年前的东西。我还是说冷静和恶心-您没有看到已经包含此代码的两个现有答案吗?感谢您提供此详细答案。这就是我发现的其他问题和答案所缺少的。如果超时后未及时返回所有值,则索引部分不足以防止列表无序。如果所有值都返回,这似乎是可行的,但我们有时会谈论10秒,这对于需要显示准确信息的解决方案来说太长了。索引实际上可以保证它们的顺序正确,但不能保证它们都存在。如果任何调用失败,Promise.all函数将返回一个被拒绝的承诺,因此您也可以捕获这些。我在问题中添加了解决方案修正语法。我认为写承诺有几种方法,但你的想法很有效。我不知道如何在Promise.all中加入多个承诺,所以我写了很长的一段时间,这对可读性很好。我使用了多个承诺来更新数据-*属性。@TrumpPaiPence我更正了语法,加入了缺少的function关键字。至于您关于.forEach不必要的查询,它被.map调用取代,该调用获取每个Price对象并将其传递给PricePromise,返回一个Promises数组,然后将该数组传递给Promise.all。
    Contract.forEach(function (Contract, index) {
        Contract.Price(function (error, result) {
            TheCurrentPrice[index]=result;
        });
    });