How To Synchronise Promise Objects?

[亡魂溺海] 提交于 2020-01-10 05:07:09

问题


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.

I have implemented some examples.This one works well. call getVal, wait 2000ms, return, i++, again call 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');
      }
    }

But I need to control an array of promise objects. What I want to do is that I have a data and I divided it 5 pieces. After first part is processed(for example:sent to server) well I want to process second part otherwise I have to process first part again.

This is the prototype implementation I made

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

Promises objects in this code works sequentially. How can i fix the code below so that it works synchronous as first example.


回答1:


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

No need to overcomplicate things. Just call await inside the loop and it'll wait for what you want.

As the other answer rightfully said - a promise represents a value and not an operation. For operations regular functions are used.

If you want to ignore failures you can .catch(() => {}) on the promise. If you want to retry until failures - you can refactor retry to a function and use that:

const retry = fn => (...args) => fn(...args).catch(retry(fn));



回答2:


If your goal is to not "execute the subsequent promises" until the first promise resolves, then you need to keep in mind that promises represent asynchronous activity already in flight. Once the promise exists, it is too late.

You need to instead not call the subsequent promise factory methods until the first promise finishes. Your first example does this by not calling getVal() until the previous promise completes.

So you end up with something like:

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



回答3:


You can use recursion, named function, .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)
});



回答4:


Well OK. I believe for the sake of proper functional programming purposes the async and await thingy should be avoided. I believe promises are very sufficient. Yet if you would like to continue coding in C++ ish imperative style then async and await is for you.

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.

Let me give a little brief on the code below. We have the async() function which takes a data and a callback (error first type). As for demo purposes it will try to invoke the callback with data within 2000ms however there is a timeout at 1000ms. So 50-50 it will either invoke the callback with data or error.

So we actually need it to return us a promise so I promisify it with the help of promisify() and it takes async() function and returns me the asyncPro() function. Which is in fact same as async() but returns a promise instead. So we are expected to use our callback at the then stage.

Then comes tryNTimes(data,asyncFun,n = 5) function which takes data, a promisified async function and an integer designating the number of times to try before rejecting it. It's default try count is 5 but you can set it to any value by passing the third argument.

As for the last part we have the flowControl() which chains up our promises perfectly by the help of Array.prototype.reduce().

So now we have all our promises chained one after the other and none will fail before trying 5 times.

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

If you would like to see the "5 times try" errors more frequently please lower the timeout value in async() function.



来源:https://stackoverflow.com/questions/39213035/how-to-synchronise-promise-objects

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!