如何同步 Promise 对象?

How To Synchronise Promise Objects?

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

我已经实施了一些 examples.This 一个效果很好。 调用 getVal,等待 2000ms,return,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');
      }
    }

但我需要控制一组 promise 对象。我想做的是我有一个数据,我把它分成 5 块。处理完第一部分后(example:sent 到服务器)我想处理第二部分,否则我必须再次处理第一部分。

这是我做的原型实现

  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');
  }
}

此代码中的承诺对象按顺序工作。我如何修复下面的代码,使其像第一个示例一样同步工作。

如果您的目标是 "execute the subsequent promises" 直到第一个承诺解决,那么您需要记住承诺代表异步 activity 已经在飞行中.一旦承诺存在,就为时已晚。

您需要改为在第一个承诺完成之前不调用后续的承诺工厂方法。您的第一个示例通过在前一个承诺完成之前不调用 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);
            }
        }
    }
}
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));

你可以使用递归,命名函数,.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)
});

好的。我相信为了正确的函数式编程目的,应该避免 asyncawait 东西。我相信承诺已经足够了。但是,如果您想继续使用 C++ 命令式风格进行编码,那么 asyncawait 适合您。

I have promise objects which need to work synchronize. For example second promise shouldn't work before first one is done. If first one rejects first one has to be executed again.

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

所以我们实际上需要它来 return 我们一个承诺,所以我在 promisify() 的帮助下承诺它,它需要 async() 函数,return 告诉我asyncPro() 函数。这实际上与 async() 相同,但 return 是一个承诺。所以我们希望在 then 阶段使用我们的回调。

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

至于最后一部分,我们有 flowControl()Array.prototype.reduce() 的帮助下完美地链接了我们的承诺。

所以现在我们将所有的 promise 一个接一个地链接在一起,none 将在尝试 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() 函数中的超时值。