如何将javascript回调流转化为承诺?

如何将javascript回调流转化为承诺?,javascript,asynchronous,promise,Javascript,Asynchronous,Promise,现在,我正在这样做: function getMentionedUsers(str, next){ var array = getUsernamesFromString(str); //['john','alex','jess']; if(array.length > 0){ var users = []; var pending = array.length; array.forEach(function(username)

现在,我正在这样做:

function getMentionedUsers(str, next){
    var array = getUsernamesFromString(str); //['john','alex','jess'];
    if(array.length > 0){
        var users = [];
        var pending = array.length;
        array.forEach(function(username){
            getUserByUsername(username).then(function(model){
                users.push(model.key);
                --pending || next(users); //this is a callback model
            }); 
        });
    }
};

function getUserByUsername(username){
    return admin.database().ref('/users').orderByChild('username').equalTo(username).once('value').then(function(snapshot) {
        return snapshot.val(); //this is the firebase example of a promise
    });
};

然而,我想把
getAttentiedUsers
变成一个承诺。我该怎么做?我不熟悉Promises

您可以使用
Promise。所有
数组#映射

    getMentionedUsers(model.body, function(users){
        console.log("Mentions", users);
    });
更具可读性的版本分为两个功能:

function getMentionedUsers(str) {
  return Promise.all(getUsernamesFromString(str).map((username) => {
    return getUserByUsername(username).then((model) => model.key);
  }));
}

使用
Promise.all

function getUserKeyByUsername(username) {
  return getUserByUsername(username).then((user) => user.key);
}

function getMentionedUsers(str) {
  const promises = getUsernamesFromString(str).map(getUserKeyByUsername);
  return Promise.all(promises);
}

你可以两者兼得。如果您传递一个
next
函数,它将被调用并显示结果。否则,您的方法将返回一个承诺

const getMentionedUsers = str =>
        Promise.all(
            getUsernamesFromString(str).map(
                username => getUserByUsername(username)
                    .then(model => model.key)
            )
        );
更新:虽然这不是您最初问题的一部分,但这仍然是一个很好的观点,值得指出。现有代码正在使用非标准回调技术。标准回调技术期望错误作为第一个参数,结果作为第二个参数:

function getMentionedUsers(str, next){
    var array = getUsernamesFromString(str); //['john','alex','jess'];
    var promise = Promise.resolve([]); // default 
    var hasNext = typeof next === 'function';
    if(array.length > 0){
        promise = Promise.all(array.map(function(username){
            return getUserByUsername(username); 
        }));
    }
    promise = promise.then(models => {
        var users = models.map(model => model.key);
        if (hasNext) next(null, users);
        return users;
    });
    if (hasNext) promise.catch(next);
    else return promise;
};

因此,我已经更新了代码,以显示“标准”回调行为和承诺。使用上面的混合方法允许现有代码保持不变,同时允许新代码使用新的Promise技术。这将被视为“非破坏性变化”。

使用本机ES6承诺,以功能性风格编写:

//返回用户名数组
函数getUsernamesFromString(str=''){
返回str.split(',').map(s=>s.trim())
}
//返回用户的承诺
函数getUserByUserName(用户名){
//假设这是一个缓慢的异步函数,并返回一个承诺
还愿({
id:(Math.random()*10**10.toFixed(0),
用户名
});
}
函数getedUsers(str){
回报你的承诺(
getUsernamesFromString(str).map(getUserByUserName)
);
}

GetAttentiedUsers('kai,ava,mia,nova')。然后(console.log.bind(console))
arrayofpromisesMessage给被否决的投票人:请证实为什么你会否决这个伟大的问题?你确定你根本不想在
数组
为空时调用
next
吗?我不建议这样做。使用此代码的回调时,将完全忽略获取用户时可能发生的任何错误。实际上,你在两个世界中都处于最糟糕的境地。我建议只使用承诺。或者,如果必须使用回调,至少要使用标准回调。@WillemD'Haeseleer-我添加了
catch
代码,这样错误就不会丢失。谢谢你指出这一点。他的原始代码也有错误。。。就这么说吧。至于选择一个而不是另一个,有些库可以做到这一点,这是一个有用的特性。而且,通过支持这两个函数,他不必重写调用此函数的所有代码。如果这是开放源代码的一部分,他就不必使用主要版本来获得新的“承诺”特性。您仍然使用非标准回调,这使得处理错误传播变得不可能。原来的代码已经被破坏的事实并不是不修复它的好理由。不过这是另一个问题,他问如何将他的函数转换为基于承诺的。从这个意义上讲,他的原始密码被破坏了,而不是我的答案。我只是向他展示了他是如何做到这两件事的——这真的很有用,而且像Mongoose这样的LIB也在使用。我不得不强烈反对,指出OP代码中的问题是OP首先发布的原因。他已经有了可用的代码,并特别询问如何才能更好地完成这项工作,因为他在该领域缺乏经验。我如何在最后获得一组用户密钥?这个答案有点过于详细,而且代码片段似乎没有任何作用。@TIMEX更新了它,以等待最后的承诺。它将返回一个用户列表,如果您想创建,您可以将其映射到ID。向上投票:)@WillemD'Haeseleer如果你和其他程序员一起工作过,你就会知道为什么它不可读。仅仅因为你可以把所有的东西放在一条线上并不意味着你应该这样做@Borna先生如果你曾经使用过函数式编程,你就会知道为什么链接方法并不那么糟糕
next(new Error(...)); //-> when something fails
next(null, results); //-> when something succeeds