5

我有需要同步工作的承诺对象。例如,在第一个承诺完成之前,第二个承诺不应该起作用。如果第一个拒绝第一个必须再次执行。

我已经实现了一些示例。这个效果很好。调用getVal,等待2000ms,返回,i++,再次调用getVal .....

 getVal() {
       return new Promise(function(resolve, reject) {
      setTimeout(function(){ resolve(19) }, 2000);
         });

     }

async promiseController(){

    for(var i =0;i<5;i++)
      {
        var _val = await this.getVal()
        console.log(_val+'prom');
      }
    }

但我需要控制一系列承诺对象。我想要做的是我有一个数据,我把它分成了5块。处理好第一部分(例如:发送到服务器)后,我想处理第二部分,否则我必须再次处理第一部分。

这是我做的原型实现

  getVal() {
   return new Promise(function(resolve, reject) {
  setTimeout(function(){ resolve(19) }, 2000);
     });

 }

async promiseController(){
  var proms=[]
  for(var i =0;i<5;i++)
    {
      proms.push(this.getVal())
    }

for(var i =0;i<5;i++)
  {
    var _val = await proms[i]
    console.log(_val+'prom');
  }
}

此代码中的 Promise 对象按顺序工作。我如何修复下面的代码,以便它作为第一个示例同步工作。

4

4 回答 4

3
async promiseController(){
  for(const value of array) {
    console.log((await this.getVal(value))+'prom');
  }
}

没有必要把事情复杂化。只需await在循环内调用,它就会等待你想要的。

正如另一个答案正确地说 - 一个承诺代表一个价值而不是一个操作。对于操作,使用常规函数。

如果你想忽略失败,你可以.catch(() => {})承诺。如果您想重试直到失败 - 您可以将重试重构为一个函数并使用它:

const retry = fn => (...args) => fn(...args).catch(retry(fn));
于 2016-08-29T19:12:02.763 回答
1

好吧。我相信为了正确的函数式编程目的asyncawait应该避免这些事情。我相信承诺是非常充分的。但是,如果您想继续以 C++ 类似的命令式风格进行编码,那么这async就是await您的理想选择。

我有需要同步工作的承诺对象。例如,在第一个承诺完成之前,第二个承诺不应该起作用。如果第一个拒绝第一个必须再次执行。

让我简要介绍一下下面的代码。我们有async()一个接受数据和回调的函数(错误第一类型)。至于演示目的,它将尝试在 2000 毫秒内使用数据调用回调,但是在 1000 毫秒时有超时。所以 50-50 它将调用带有数据或错误的回调。

所以我们实际上需要它来给我们一个承诺,所以我在它的帮助下承诺它,promisify()它接受async()函数并将函数返回给我asyncPro()。这实际上与async()但返回一个承诺相同。所以我们应该在这个then阶段使用我们的回调。

然后是tryNTimes(data,asyncFun,n = 5)接收数据的函数,一个承诺的异步函数和一个整数,指定在拒绝之前尝试的次数。它的默认尝试计数为 5,但您可以通过传递第三个参数将其设置为任何值。

至于最后一部分,我们flowControl()通过Array.prototype.reduce().

所以现在我们将所有的承诺一个接一个地链接起来,在尝试 5 次之前没有一个会失败。

function promisify(fun){
  return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}

function async(data, callback){
  var dur = Math.floor(Math.random()*2000);
  setTimeout(_ => callback(false,data),dur);           // may resolve before timeout
  setTimeout(_ => callback("error at " + data),1000);  // timeout at 1 sec
}

function tryNTimes(data,asyncFun,n = 5){
  return new Promise((resolve,reject) => { n === 0 && reject("try out fail at 5 tries: " + data);
                                           asyncFun(data).then(v => resolve("resolved at countdown " + n + ": " + v))
                                                         .catch(e => resolve(tryNTimes(data,asyncFun,--n)));
                                         });
}

function flowControl(d,f,tc){
  return d.reduce((prom,chunk) => prom.then(v => { console.log(v);
                                                   return tryNTimes(chunk,f,tc);
                                                 }),Promise.resolve("initial dummy promise"));
}

var data = ["chunk_1", "chunk_2", "chunk_3", "chunk_4", "chunk_5"],
asyncPro = promisify(async);                           // now our async function returns a promise

flowControl(data,asyncPro).then(v => console.log(v))
                          .catch(e => console.log(e));

如果您想更频繁地看到“5 次尝试”错误,请降低async()函数中的超时值。

于 2016-08-30T00:35:57.867 回答
1

如果您的目标是在第一个 Promise 解决之前不“执行后续的 Promise”,那么您需要记住 Promise 代表已经在运行的异步活动。一旦承诺存在,就为时已晚。

在第一个 Promise 完成之前,您需要不要调用后续的 Promise 工厂方法。getVal()您的第一个示例通过在前一个承诺完成之前不调用来做到这一点。

所以你最终会得到类似的东西:

delay(time) {
    return new Promise(resolve => setTimeout(resolve, time));
}

async promiseController() {
    const factories = [];
    for (let i = 0; i < 5; ++i) {
        factories.push(() => this.getVal());
    }

    for (const f of factories) {
        // keep running this factory until it succeeds
        let success = false;
        while (!success) {
            try {
                const promise = f();
                const result = await f;
                success = true;
                console.log(`result = ${result}`);
            }
            catch (err) {
                console.log("promise failed.  retrying");
                await delay(100);
            }
        }
    }
}
于 2016-08-29T18:58:21.550 回答
1

您可以使用递归、命名函数、.then()

var arr = [Promise.resolve("a")
           , Promise.resolve("b")
           , Promise.resolve("c")];
var i = 0;
var res = [];

function foo() {
  // conditional resolved or rejected promise
  var n = String(new Date().getTime()).slice(-1);
  // if `n` < 5 reject `n` , else resolve `n`
  var curr = n < 5;
  return curr ? arr[i] : Promise.reject(["rejected", n])
}

var p = (function repeat() {
  var promise = foo();
  return promise
    .then(function(data) {
      console.log(data);
      res.push(data);
      ++i;
      if (i < arr.length) return repeat()
      // return `res` array when all promises complete
      else return res
    })
    .catch(function(err) {
      console.log(err);
      if (err[0] === "rejected") return repeat()
    })
}());

p.then(function(complete) {
  console.log("complete:", complete)
});

于 2016-08-29T19:35:17.837 回答