当多个匿名承诺评估时做一些事情

时间:2021-08-01 15:15:07

标签: javascript loops for-loop promise es6-promise

我正在尝试弄清楚 Promise 是如何工作的,我已经理解了一个基本概念,但我真的不明白我应该如何等待多个 Promise 完成。特别是因为它们是动态创建的,一个接一个。

如果有人能为我指明更优雅的解决方案,我会很高兴。

简而言之,我需要的是:

  • 遍历对象数组,(迭代)
  • 检查它是否存在于 DB*(异步)中
  • 如果存在,更新 DB* 中的数据(异步)
  • 如果它不存在,将新对象添加到 DB*(异步)
  • 完成所有这些后,再做其他事情

*我在我的应用程序中使用 localforage,但我正在使用 setTimeout 模拟它。

我想过使用这种方法进行序列迭代: JavaScript ES6 promise for loop

for (let i = 0, p = Promise.resolve(); i < 10; i++) {
    p = p.then(_ => new Promise(resolve =>
        setTimeout(function () {
            console.log(i);
            resolve();
        }, Math.random() * 1000)
    ));
}

这很棘手,因为我没有在此迭代完成时创建所有承诺,所以我需要单独跟踪它们(承诺)并且只在它们出现时运行“Promise.allSettled”。

这是我的测试小提琴 https://jsfiddle.net/mefistofelos/2ergotvw/

此处为完整代码:

let app = {
  "data": {
    "arr" : [5,10,15,20,25,30,35,"1.oni"],
    "ids" : [1,2/*,3,4,5,6,7,8,9,10*/,100],
    "items" : [{"id": 1, "name": "apple"},{"id": 2, "name": "pear"},{"id": 3, "name": "plum"},{"id": 4, "name": "ananas"},{"id": 17, "name": "brick"}],
    getItem : (id, ptIndex) => new Promise(
      resolve => setTimeout(
        () => {
          let resp = {"id": id, "data": [], "ptIndex" : ptIndex};
          resp.data = app.data.items.filter( (ob) => ob.id == id );
          console.log("id: " + id + " ptIndex: " + ptIndex, resp);
          resolve(resp)
        }
      , Math.floor((Math.random() * 1500) + 300))
    ),
    setItem : (data) => new Promise(
      (resolve) => setTimeout(
        () => {
          let resp = data;
          resp.status = "success";
          app.promisesSequence._items.push(data);

          console.log("setItem Resp Data", data);

          return resolve(resp);
        }
      , Math.floor((Math.random() * 2500) + 800))
    )
  },
  // build a set of anonymous functions and run them in sequence
  "promisesSequence": {
    // holds promise functions
    "_items" : [],
    /*"_track" : [],*/
    "_promiseArr" : [],
    // initiate promise tracking
    "track" : {
      "multiplier" : 2,
      "data" : [],
      start : (total) => {
        app.promisesSequence.track.data.push({"total": total,"arr":[]});
        return app.promisesSequence.track.data.length-1;
      },
      // add promise to the promise tracking tree
      addPromise : (ptIndex, promise) => {
        app.promisesSequence.track.data[ptIndex].arr.push(promise);
        return promise;
      },
      isReady : (ptIndex) => (app.promisesSequence.track.getTotal(ptIndex) == app.promisesSequence.track.getTracked(ptIndex) ),
      getTotal : (ptIndex) => app.promisesSequence.track.data[ptIndex].total*app.promisesSequence.track.multiplier,
      getTracked : (ptIndex) => app.promisesSequence.track.data[ptIndex].arr.length,
      getPromises : (ptIndex) => app.promisesSequence.track.data[ptIndex].arr,
    },
    onFinishCb: _ => {
      console.log("[onFinishCb]: Script finished, ("+app.promisesSequence._items.length+") items added: ", app.promisesSequence._items);
    },
    // build promise function array
    build: _ => {
      console.log("init promisesSequence");

      // start tracking items
      let ptIndex = app.promisesSequence.track.start(app.data.ids.length);
      console.log("ptIndex: " + ptIndex);
      // go through array
      for (let i = 0, p = Promise.resolve(); i <= app.data.ids.length-1; i++) {
        console.log("get Item " +app.data.ids[i]+" Data: " );
        p = p.then( _ => {

          app.promisesSequence.track.addPromise(ptIndex,app.data.getItem(app.data.ids[i], ptIndex)).then(
            (resp) => {
              console.log("GOT item (" + resp.id + ") data: ", resp.data);

              if ( resp.data.length >= 1 ) {
                item = resp.data[resp.data.length-1];
                item.edited = true;
                item.a = "edit";
              } else {
                item = {"id": resp.id, "data": [], "name" : "", "a" : "insert"};
              }

              // pass promiseTreeIndex
              item.ptIndex = resp.ptIndex;

              console.log("Try to Set item ("+item.id+"): ",item);

              app.promisesSequence.track.addPromise(item.ptIndex, app.data.setItem(item)).then (
                (resp) => console.log("Item ("+resp.id+") is Set.", resp)
              )

              // try to bind CB after the last of the GET requests have been created
              if ( app.promisesSequence.track.isReady(item.ptIndex) === true ) {
                console.log("**Tracked Promises: " + app.promisesSequence.track.getTracked(item.ptIndex) + " From Total of " + app.promisesSequence.track.getTotal(item.ptIndex));
                console.log("Call Promise.allSettled");
                Promise.allSettled(app.promisesSequence.track.getPromises(item.ptIndex)).then(app.promisesSequence.onFinishCb);
              }

              return i;
            })
        })
      }

      console.log("iteration complete");
    },

    run: () => {
      return app.promisesSequence.build();
    }
  }
}


app.promisesSequence.run();



如您所见,它是按顺序添加项目,并且我在“GET”和“SET”函数中都有正确的数据,所以我已经弄清楚了。

我的问题: 有没有比我想出的更专业的方法呢? 我希望避免跟踪我创建的每一个承诺。通过更进一步,我可以手动检查它们是否已完成,这样我什至不需要使用 .allSettled 方法。

1 个答案:

答案 0 :(得分:0)

await/async 可能正是我想要的。 这样它就更短了,而且几乎与我昨天在问题中写的一样。

但是......第一轮检查不是异步的......它们不是并行检查......我想我需要更多地考虑这个......

let app = {
  "data": {
    "ids" : [1,2,3,4,5,6,7,8,9,10,100],
    "items" : [{"id": 1, "name": "apple"},{"id": 2, "name": "pear"},{"id": 3, "name": "plum"},{"id": 4, "name": "ananas"},{"id": 17, "name": "brick"}],
    getItem : (id) => new Promise(
      resolve => setTimeout(
        () => {
          let resp = {"id": id, "data": []};
          resp.data = app.data.items.filter( (ob) => ob.id == id );
          //console.log("id: " + id , resp);
          resolve(resp)
        }
      , Math.floor((Math.random() * 1500) + 300))
    ),
    setItem : (data) => new Promise(
      (resolve) => setTimeout(
        () => {
          let resp = data;
          resp.status = "success";
          app.promisesSequence._items.push(data);

          //console.log("setItem Resp Data", data);

          return resolve(resp);
        }
      , Math.floor((Math.random() * 2500) + 800))
    )
  },

  // build a set of anonymous functions and run them in sequence
  "promisesSequence": {
    // holds promise functions
    "_items" : [],
    onFinishCb: _ => {
      console.log("Script finished, ("+app.promisesSequence._items.length+") items added: ", app.promisesSequence._items);
    },
    // build promise function array
    build: async _ => {
      console.log("init promisesSequence");
      // go through array
      for (let i = 0; i <= app.data.ids.length-1; i++) {
        console.log("get Item: " +app.data.ids[i] );
        
        var result = await app.data.getItem(app.data.ids[i]);
        console.log( "["+app.data.ids[i]+"] WE HAVE THE ITEM");
        if ( result.data.length == 1 ) {
          item = result.data[result.data.length-1];
          item.a = "edit";
        } else {
          item = {"id": result.id, "data": [], "a" : "insert"};
        }
        console.log("Set item: "+item.id);
        var result = await app.data.setItem(item);
        console.log( "["+app.data.ids[i]+"] ITEM DATA AFTER INSERT: ", result);
      }
      console.log("iteration complete, trigger onFinishCb now");
      app.promisesSequence.onFinishCb();
    },

    run: () => {
      return app.promisesSequence.build();
    }
  }
}

app.promisesSequence.run();

https://jsfiddle.net/mefistofelos/9ocyqbuL/