2017-05-24 39 views
0

我有这个小计划,通过乘以费率和几个小时来计算总计。函数返回foreach结束之前的地图

我遇到的问题是函数getTasks()总是返回一个空的地图。 当我登录在地图中输入的字段时,它们不是空的,而是在函数返回地图后输入。 所以我有点困惑为什么会发生这种情况。

function getTask(taskId) { 
    return rp({ 
    uri: `${url}/tasks/${taskId}`, 
    auth: { 
     user: 'user', 
     password, 
     sendImmediately: false 
    }, 
    json: true 
    }).then((task) => { 
    return task; 
    }); 
} 

function getTasks(entries) { 
    const taskIds = []; 

    entries.forEach((entry) => { 
    const taskId = entry.day_entry.task_id; 
    if (!taskIds.includes(taskId)) { 
     taskIds.push(taskId); 
    } 
    }); 

    const taskRateMap = new Map(); 

    taskIds.forEach((taskId) => { 
    return Promise.resolve(getTask(taskId)).then((res) => { 
     if (res.task.id === taskId) { 
     taskRateMap.set(taskId, res.task.default_hourly_rate); 
     console.log(taskRateMap); 
     } 
    }); 
    }); 
    return taskRateMap; 
} 

function calculateTasksTotals(id) { 
    return co(function* calculateTotalsGen() { 
    let total = 0; 

    const entries = yield getEntriesForProject(id); 

    const tasks = getTasks(entries); 

    entries.forEach((entry) => { 
     const rate = tasks.get(entry.day_entry.task_id); 
     total += rate * entry.day_entry.hours; 
    }); 
    return total; 
    }); 
} 

calculateTasksTotals(id) 

回答

0

有你的代码中的多个问题:

  1. 首先,只要你有参与功能的异步操作,该功能的结果将是异步的。你根本无法同步返回它。异步操作稍后结束。该函数本身在异步操作完成之前返回。

  2. 因此,您从使用异步操作的任何函数返回一个承诺,并且调用者使用该承诺知道何时完成或获得最终结果。

  3. 您的功能getTask()没问题。它返回一个承诺。该功能内的.then()是多余的,并且不需要,因为task似乎已经是承诺的解决价值。

  4. 你的功能getTasks()试图同步返回taskRateMap,但你已经在测试中看到的,所以有在taskRateMap没有价值观又没有承诺还没有解决。在我的代码版本中,我在内部使用Promise.all()来知道何时完成了所有的getTask()操作,并且我返回了已解析值的承诺是taskRateMap对象。

  5. getTasks()的调用者可以使用该承诺和.then()处理程序来访问taskRateMap对象。

下面是实现的一种方式getTasks()

function getTasks(entries) { 
    // get all unique task_id values from the entries array 
    const taskIds = Array.from(new Set(entries.map(entry => entry.day_entry.task_id))); 
    const taskRateMap = new Map(); 

    // use Promise.all() to know when the whole array of promises is done 
    // use tasksIds.map() to build an array of promises 
    return Promise.all(taskIds.map(taskId => { 
     // make this promise be the return value inside the .map() callback 
     // so we will end up with an array of promises that will be passed to 
     // Promise.all() 
     return getTask(taskId).then(res => { 
      if (res.task.id === taskId) { 
       taskRateMap.set(taskId, res.task.default_hourly_rate); 
      } 
     }) 
    })).then(() => { 
     // make final resolved value be the taskRateMap 
     return taskRateMap; 
    }); 
} 

getTasks(entries).then(taskRateMap => { 
    // use taskRateMap here 
}); 
0

您的承诺存在问题。尝试使用Promise.all()提供所有承诺作为输入,并且只有在所有承诺解决后才返回地图。 否则直接返回你的Promise.all()并在调用方法中创建地图。

因此,像:

const tasksPromises = []; 
    taskIds.forEach((taskId) => { 
    tasksPromises.push(getTask(taskId)); 
    }); 
return Promise.all(tasksPromises); 

然后你调用的方法内部通过then解决的承诺,其中每个元素是相应承诺的返回值,你将有作为回调函数的参数数组。

0

我相信会发生这种情况,因为taskRateMap在返回之前未被填充。你可能想看看Promise.all() 并考虑包装

promises = taskIds.map((taskId) => { 
return Promise.resolve(getTask(taskId)).then((res) => { 
    if (res.task.id === taskId) { 
    return [taskId, res.task.default_hourly_rate]; 
    console.log(taskRateMap); 
    } 
}); 

return Promise.all(promises).then(v => /* taskRateMap*/)