如何在嵌套for循环中链接promises?

时间:2018-01-09 22:46:48

标签: javascript node.js for-loop promise

var verifyEmail = function (thisEmail){
    return new Promise(
        function (resolve, reject) {
            quickemailverification.verify(thisEmail, function (err, response) {
              // Print response object
              console.log(response.body);
              if (response.body["success"] == "true"){
                  var validity = response.body["result"];
                  if (validity == "valid"){
                    console.log("Email Valid!");
                    resolve(validity);
                  } else {
                    console.log("Email Invalid!")
                    resolve(validity);
                  }
              } else {
                  var reason = new Error("API unsuccessful");
                  reject(reason);
              }
            });
        }
    );
};

var saveValidity = function (validity){
    return new Promise(
        function (resolve, reject){
            if (validity == "valid"){
                var state = true;
                admin.database().ref("/users_unverified/"+keys[i]+"/emails/"+x+"/verified/").set(state, function(error) {
                  if (error) {
                    console.log("Email ("+thisEmail+") verfication could not be saved" + error);
                  }
                  console.log("Email verification saved: " +thisEmail);
                });
            } else {
                state = false;
                admin.database().ref("/users_unverified/"+keys[i]+"/emails/"+x+"/verified/").set(state, function(error) {
                  if (error) {
                    console.log("Email ("+thisEmail+") verfication could not be saved" + error);
                  }
                  console.log("Email verification saved: " +thisEmail);
                });
            }
        }
    );
};

admin.database().ref("/users_unverified/").once('value').then(function(snapshot) {
    var snap = snapshot.val();
    keys = Object.keys(snap);

    for (var i = 0; i < 100; i++){
        var emails = snap[keys[i]]["emails"];
        if (emails){
            for (var x = 0; x<emails.length; x++){
                var thisEmail = emails[x]["email"];
                var emailVerified = emails[x]["verified"];
                if (emailVerified != true || emailVerified != false){
                    verifyEmail
                        .then(saveValidity)
                        .then(function (fulfilled) {
                            console.log(fulfilled);
                        })
                        .catch(function (error){
                            console.log(error.message);
                        });
                }
            }
        }
    }
});

以上是我放在一起的代码。我不太相信它会起作用。我是承诺的新手,所以我试图理解如何做到这一点。

verifyEmail函数应该从代码的第三个块中的firebase查询中获取电子邮件地址。 saveValidity函数应该接受来自verifyEmail的有效性响应。

但是,我还担心我在firebase查询块中嵌套的for循环。我正在遍历每个用户以验证他们的电子邮件,但每个用户有时也会有多封电子邮件。我担心它会在完成检查前一个用户的所有电子邮件之前循环到下一个用户。

我也不确定我是否可以按照我的方式将数据传递到promise函数。

绝对可以在这里使用一些帮助。真的很难理解它是如何工作的。

2 个答案:

答案 0 :(得分:1)

首先,您需要修复saveValidity()以始终解析或拒绝承诺,并传入其引用的其他变量keythisEmail

const saveValidity = function (validity, key, thisEmail){
    return new Promise(
        function (resolve, reject){
            if (validity == "valid"){
                let state = true;
                admin.database().ref("/users_unverified/"+key+"/emails/"+x+"/verified/").set(state, function(error) {
                  if (error) {
                    let msg = "Email ("+thisEmail+") verfication could not be saved" + error;
                    console.log(msg);
                    reject(new Error("Email ("+thisEmail+") verfication could not be saved" + error));
                  } else {
                    resolve("Email verification saved: " +thisEmail);
                  }
                });
            } else {
                state = false;
                admin.database().ref("/users_unverified/"+keys[i]+"/emails/"+x+"/verified/").set(state, function(error) {
                  if (error) {
                    let msg = "Email ("+thisEmail+") verfication could not be saved" + error;
                    console.log(msg);
                    reject(new Error(msg));
                  } else {
                    resolve("Email verification saved: " +thisEmail);
                  }
                });
            }
        }
    );
};

然后,对您的主循环进行了一些更改:

  1. 我假设我们可以并行运行所有verifyEmail()个调用,因为它们似乎没有任何关系。
  2. verifyEmail.then(...)更改为verifyEmail(thisEmail).then(...)`实际调用该函数
  3. 收集数组中的所有verifyEmail()承诺
  4. 在承诺数组上调用Promise.all()以监控它们何时完成
  5. .then()返回值,以便我们在Promise.all()
  6. 中获取返回的值
  7. 重新考虑.catch(),因此保证会被拒绝,并会过滤回Promise.all()。如果你想忽略它们并继续其他人,你可能会在这里吃错误。
  8. var切换到let
  9. !=更改为!==,因为看起来您明确要查找truefalse值,并且不希望进行类型转换。
  10. 传入saveValidity()需要的变量。
  11. 在比较emailVerified时更改逻辑,因为之前的内容始终是正确的,因此可能不是正确的逻辑。我想你想要知道什么时候emailVerified尚未设置为truefalse,这意味着您必须使用&&,而不是||
  12. for圈与keys.length进行比较,而不是100的硬编码值。
  13. 而且,这是主嵌套for循环的结果代码:

    admin.database().ref("/users_unverified/").once('value').then(function(snapshot) {
        let snap = snapshot.val();
        let keys = Object.keys(snap);
    
        let promises = [];
        for (let i = 0; i < keys.length; i++){
            let key = keys[i];
            let emails = snap[key]["emails"];
            if (emails){
                for (let x = 0; x < emails.length; x++) {
                    let currentKey = key;
                    let thisEmail = emails[x]["email"];
                    let emailVerified = emails[x]["verified"];
                    if (emailVerified !== true && emailVerified !== false){
                        promises.push(verifyEmail(thisEmail).then(validity => {
                            return saveValidity(validity, currentKey, thisEmail);
                        }).then(function (fulfilled) {
                            console.log(fulfilled);
                            return fulfilled;      // after logging return value so it stays the resolved value
                        }).catch(function (error) {
                            console.log(error.message);
                            throw error;           // rethrow so promise stays rejected
                        }));
                    }
                }
            }
        }
        return Promise.all(promises);
    }).then(results => {
        // all results done here
    }).catch(err => {
        // error here
    });
    

答案 1 :(得分:1)

如果ES2017适用于您的情况,您只需使用关键字awaitasync即可直接执行此操作。以下是一个例子:

function resolveAfter2Seconds(x) { 
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

async function f1() {
  var x = await resolveAfter2Seconds(10);
  console.log(x); // 10
}
f1();

您可以阅读有关async / await here的更多信息。

如果您希望在没有async / await的情况下执行此操作以获得更好的浏览器兼容性,则可以使用Babel进行预编译。

如果您真的想要一个lightwight实现,可以使用名为chainPromiseThunks的函数,或简称chain。这个chain函数接受一组Promises Thunk,并返回一个新的Thunk of Promise,以下是chain的一行实现:

const chain = thunks => thunks.reduce((r, a) => () => r().then(a));

这是一个用法演示:

const echo = x =>
    new Promise(function(resolve) {
        return setTimeout((function() {
            console.log(x);
            return resolve(x);
        }), 1000);
    })
;

const pThunks = [1,2,3,4,5].map(i => () => echo(i));

chain(pThunks)();