Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 执行顺序问题_Javascript - Fatal编程技术网

Javascript 执行顺序问题

Javascript 执行顺序问题,javascript,Javascript,在我下面的代码中,5总是在4之前打印。我认为,因为对positioners的回调在matchAgainstAD的返回语句中,它将等待for循环和ad查找完成后再返回。我怎样才能以最简单的方式做到这一点 var matchAgainstAD = function(stUsers) { stUsers.forEach(function (element, i) { var sAMAccountName = stUsers[i].guiLoginName; // Find u

在我下面的代码中,5总是在4之前打印。我认为,因为对
positioners
的回调在
matchAgainstAD
的返回语句中,它将等待for循环和ad查找完成后再返回。我怎样才能以最简单的方式做到这一点

var matchAgainstAD = function(stUsers) {

  stUsers.forEach(function (element, i) {

    var sAMAccountName = stUsers[i].guiLoginName;

    // Find user by a sAMAccountName
    var ad = new ActiveDirectory(config);

    ad.findUser(sAMAccountName, function(err, user) {

      if (err) {
        console.log('ERROR: ' +JSON.stringify(err));
        return;
      }

      if (!user) {
        staleUsers.push(stUsers[i])
        console.log(4)
      }
      // console.log(staleUsers);
    });
  })
  return postUsers(staleUsers)
}

var postUsers = function(staleUsers) {
  console.log(5);

  request.post({
    headers: {'content-type' : 'application/x-www-form-urlencoded'},
    url: 'http://localhost:8000/api/record/newRecord',
    qs: staleUsers
  }, function(err, res, body) {
    // console.log(body);
  })
}

matchAgainstAD();

ad.findUser
接受包含
console.log(4)
的回调。该函数是异步的,当IO操作完成时,它将命中您的回调

另一方面,
positioners
是完全同步调用的,因此在
ad.findUser
进入回调之前,它将点击
console.log(5)

解决此问题的一个简单方法是从
ad.findUser
回调内部调用
positioners


我建议研究JavaScript的承诺模式来管理异步操作之间的依赖关系。有几种流行的库(Q和RSVSP.js是其中的一对)。

这是node.js中非常典型的异步问题。您的
findUser()
函数有一个异步响应,这意味着回调将在稍后调用。同时,循环的其余部分将继续运行,以便所有请求都同时进行,然后响应将在稍后某个时间开始。因此,在
matchAgainstAd()
返回后不能调用
positioners()
,因为内部异步操作尚未完成,因此
staleUsers
尚未填充

解决这个问题有多种方法。一般来说,值得学习如何为这样的操作使用承诺,因为它们在使用异步操作时提供了各种非常有用的控制流选项,node.js几乎将所有I/O操作都作为异步操作来执行。但是,为了最好地说明这类问题中发生了什么,我将首先向您展示一个手动编码的解决方案。在这个手动编码的解决方案中,您可以跟踪还有多少操作要完成,以及何时所有操作都已完成,然后,也只有在那时,您才能使用累积的数据调用
positioners()

手动编码的解决方案

var matchAgainstAD = function (stUsers) {
    var remaining = stUsers.length;
    stUsers.forEach(function (element, i) {
        var sAMAccountName = stUsers[i].guiLoginName;

        function checkDone() {
            if (remaining === 0) {
                postUsers(staleUsers);
            }
        }
        // Find user by a sAMAccountName
        var ad = new ActiveDirectory(config);
        ad.findUser(sAMAccountName, function (err, user) {
            --remaining;
            if (err) {
                console.log('ERROR: ' + JSON.stringify(err));
                checkDone();
                return;
            }
            if (!user) {
                staleUsers.push(stUsers[i])
            }
            checkDone();
        });
    });
}

var postUsers = function(staleUsers) {

  request.post({
    headers: {'content-type' : 'application/x-www-form-urlencoded'},
    url: 'http://localhost:8000/api/record/newRecord',
    qs: staleUsers
  }, function(err, res, body) {
    // console.log(body);
  })
}
这里的核心逻辑是,根据要执行的操作数初始化计数器。然后,在每个操作发生的循环中,只要一个操作完成(调用它的完成回调),您就会减少剩余的
计数器。然后,在处理结果(在成功和错误代码路径中)后,检查剩余的
计数是否已达到0,这表示所有请求现在都已完成。如果是这样,那么
staleUsers
数组现在已完全填充,您可以调用
postures(staleUsers)
来处理累积结果

使用蓝鸟承诺编码的解决方案

这里的想法是,我们使用控制流逻辑和承诺的增强错误处理来管理异步控制流。这是通过“提示”我们在这里使用的每个异步接口来实现的。“Promisizing”是围绕遵循node.js调用约定的任何异步函数创建一个小函数包装器的过程,其中函数的最后一个参数是一个回调,它至少接受两个参数,第一个参数是错误,第二个参数是值。这可以自动转换为一个包装函数,返回一个承诺,允许使用承诺逻辑流进行任何正常的异步操作

下面是使用bluebird promise库的工作方式

var Promise = require('bluebird');
var request = Promise.promisifyAll(request('require'));

var matchAgainstAD = function (stUsers) {

    var staleUsers = [];
    var ad = new ActiveDirectory(config);
    // get promisified version of findUser
    var findUser = Promise.promisify(ad.findUser, ad);

    return Promise.map(stUsers, function(userToSearchFor) {
        var sAMAccountName = userToSearchFor.guiLoginName;
        return findUser(sAMAccountName).then(function(user) {
            // if no user found, then consider it a staleUser
            if (!user) {
                staleusers.push(userToSearchFor);
            }
        }, function(err) {
            // purposely skip any requests we get an error on
            // having an error handler that does nothing will
            // stop promise propagation of the error (it will be considered "handled")
        });
    }).then(function() {
        if (staleUsers.length) {
            return postUsers(staleUsers);
        }
        return 0;
    });
}


var postUsers = function (staleUsers) {
    return request.postAsync({
        headers: {
            'content-type': 'application/x-www-form-urlencoded'
        },
        url: 'http://localhost:8000/api/record/newRecord',
        qs: staleUsers
    }).spread(function (res, body) {
        // console.log(body);
        return staleUsers.length;
    })
}

matchAgainstAD(users).then(function(qtyStale) {
    // success here
}, function(err) {
    // error here
})
标准ES6承诺版本

var matchAgainstAD = function (stUsers) {
    var remaining = stUsers.length;
    stUsers.forEach(function (element, i) {
        var sAMAccountName = stUsers[i].guiLoginName;

        function checkDone() {
            if (remaining === 0) {
                postUsers(staleUsers);
            }
        }
        // Find user by a sAMAccountName
        var ad = new ActiveDirectory(config);
        ad.findUser(sAMAccountName, function (err, user) {
            --remaining;
            if (err) {
                console.log('ERROR: ' + JSON.stringify(err));
                checkDone();
                return;
            }
            if (!user) {
                staleUsers.push(stUsers[i])
            }
            checkDone();
        });
    });
}

var postUsers = function(staleUsers) {

  request.post({
    headers: {'content-type' : 'application/x-www-form-urlencoded'},
    url: 'http://localhost:8000/api/record/newRecord',
    qs: staleUsers
  }, function(err, res, body) {
    // console.log(body);
  })
}
这里有一个版本,它只使用node.js中内置的标准ES6承诺。这里的主要区别在于,您必须编写您自己想要使用的异步函数的promisify版本,因为您不能使用Bluebird中的内置promisify功能

var request = request('require');

// make a promisified version of request.post
function requestPostPromise(options) {
    return new Promise(function(resolve, reject) {
        request.post(options, function(err, res, body) {
            if (err) {
                reject(err);
            } else {
                resolve([res, body]);
            }
        });
    });    
}

// make a function that gets a promisified version of ad.findUser
function getfindUserPromise(ad) {
    return function(name) {
        return new Promise(function(resolve, reject) {
            ad.findUser(name, function(err, user) {
                if (err) {
                    reject(err);
                } else {
                    resolve(user);
                }
            });
        });
    }
}

var matchAgainstAD = function (stUsers) {

    var staleUsers = [];
    var promises = [];
    var ad = new ActiveDirectory(config);
    // get promisified version of findUser
    var findUser = getFindUserPromise(ad);

    stUsers.each(function(userToSearchFor) {
        promises.push(findUser(userToSearchFor.guiLoginName).then(function(user) {
            // if no user found, then consider it a staleUser
            if (!user) {
                staleusers.push(userToSearchFor);
            }
        }, function(err) {
            // purposely skip any requests we get an error on
            // have an error handler that does nothing will
            // stop promise propagation of the error (it will be considered "handled")
        }));
    });
    return Promise.all(promises).then(function() {
        if (staleUsers.length) {
            return postUsers(staleUsers);
        }
        return 0;
    });
}


var postUsers = function (staleUsers) {
    return requestPostPromise({
        headers: {
            'content-type': 'application/x-www-form-urlencoded'
        },
        url: 'http://localhost:8000/api/record/newRecord',
        qs: staleUsers
    }).then(function (err, results) {
        var res = results[0];
        var body = results[1];
        // console.log(body);
        return staleUsers.length;
    })
}

matchAgainstAD(users).then(function(qtyStale) {
    // success here
}, function(err) {
    // error here
})

active directory接口是异步的。操作完成时将调用回调。JavaScript通常没有“等待”的概念。关于解决类似问题的各种选项的讨论,请参见下面的答案:我现在明白了,我的问题是我不知道如何纠正它。意思是,在
ad.findUser
完成后,我如何使5执行?@CiscoKidx这个答案是正确的。我建议您在
forEach
中创建一个计数器,当计数器到达
stUsers
的长度时,您可以调用
postasters(staleUsers)
。这将是最后一次迭代。谢谢你的回答。一个承诺的例子(在这种情况下)会很有帮助。我担心这就是解决方案。@CiscoKidx-我添加了使用蓝鸟承诺的第二个版本。@CiscoKidx-我添加了使用标准ES6承诺的第三个版本。