【问题标题】:How do you push asynchronous responses into an array in the order they were requested?如何按照请求的顺序将异步响应推送到数组中?
【发布时间】:2018-02-01 08:37:18
【问题描述】:

我正在使用 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,这是正确的语法。

【问题讨论】:

  • 用增量 ID 标记请求并推送到数组的该索引。 arr[id] = result
  • 'Promise.all: Order of resolved values'线程我在问之前遇到过。这显然是答案的一部分,但 Alnitak 正确回答了特定用例。另一个线程更抽象,答案主要是行话,需要 ES6 之后的当前 JS 知识,这是最近的东西(三年前)。我还是说很酷很恶心。 :-)

标签: javascript arrays asynchronous foreach


【解决方案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 等 Promise 库包含自动“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 添加了一个)包括附加功能,允许您限制将发出的并发调用的数量。

【讨论】:

  • 感谢您的详细解答。这是我发现的其他问题和答案所缺少的。如果超时后没有及时返回所有值,则索引部分不足以防止列表乱序。如果值全部返回,这似乎可行,但我们有时会说 10 秒,这对于需要显示准确信息的解决方案来说太长了。
  • 索引实际上会保证它们的顺序正确,但不能保证它们都存在。如果任何调用失败,Promise.all 函数将返回一个 rejected 承诺,因此您也可以.catch 那些。
  • 我在我的问题中添加了解决方案更正语法。我认为有几种写承诺的方法,但你的想法很有效。我不知道如何在 Promise.all() 中放置多个 Promise,所以我写了很长一段路,这对可读性来说还是不错的。我使用了多个 promise 来更新 data-* 属性。
  • @TrumpPaiPence 我更正了语法,添加了缺少的 function 关键字。至于您对.forEach 的查询不是必需的,它被.map 调用取代,该调用将每个Price 对象传递给PricePromise,返回一个Promises 数组,然后传递给Promise.all.
【解决方案2】:

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

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

        TheCurrentPrice[index] = result;

    });
});

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 您没有看到已经包含此代码的两个现有答案吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 1970-01-01
    • 2017-12-30
    • 2016-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多