使用promises在for循环中请求依赖于顺序的信息

时间:2016-04-17 00:11:08

标签: javascript promise bluebird

我的任务相对简单;现有信息已过期,因此我必须从API请求信息,修改现有信息文件并将其推回服务器并进行更新。信息采用json文件的形式;很简单。该文件包含一个对象,该对象具有一些对象,这些对象具有必须更新的多个属性。这就是问题发生的地方;对象数组生成一个API请求数组,其响应必须与产生请求的原始对象匹配(因为响应包含必须在对象中更新的信息)。

这是我迄今为止所做的承诺的要点:

function main() {
    // First get existing data.
	getExistingData().then(function(result) {
	  console.log(result); // It worked, return it for next 'then' to use.
	  return result;
	}, function(err) {
	  console.log(err); // This usually never happens.
	}).then(function(result) { // Use the existing data to generate the requests for new data.
		requestNewData(result).then(function(moddedJson) {
			console.log(moddedJson); // This happens BEFORE I get responses back from the request, which is wrong.
		});
	});
}

function getExistingData() {
	return new Promise(function(resolve, reject) {
		fetch('dataURLHere')
		.then(function(res) {
	        resolve( res.json()); // Turn result into JSON, and return it.
	    })
	})
}

function requestNewData(rawJson) {
	return new Promise(function(resolve) {
        // Loop over the number of objects in the original data.
		for (var i = 0; i < rawJson.length; i++) { 
            // Loop over the array of objects within each object.
	    	for (var multiId = 0; multiId < rawJson.hits.length; multiId++) {
	    		var requestUrl = "someURLConstructedFromJsonData";
                var hit = rawJson.hits[multiId];
				new Promise(function(resolve) {
					request(requestUrl, function(error, response, body) {
					  	if (!error && response.statusCode == 200) {
                            // Need to parse the XML response into a js object.
					  		parseString(body, function (err, result) {
                                hit.propertyToChange = result.propertyToChange;
                                hit.propertyToChange2 = result.propertyToChange2;
							});
					  	}
					  	else {
					  		console.log("No data for this item.");
					  	}
						resolve(hit);
					});
				})
			}
		}
		resolve(rawJson);
	})
}

基本上,我想要发生的事情是: 1)获取原始数据。这很容易,我的代码已经完成了。 2)使用原始数据为数据中的每个文档以及每个文档中的每组属性生成请求。这也不是问题。 3)确保来自请求的返回数据与现有数据匹配。这是我无法解决的问题部分。

1 个答案:

答案 0 :(得分:0)

问题在于你过早解决问题。

红旗是指您创建承诺但从未对其做任何事情:

new Promise(function(resolve) {
    request(requestUrl, function(error, response, body) {
    ...

该承诺确实得到了正确解决,但没有人在等待它。简单的解决方案是Promise.all:

function requestNewData(rawJson) {
    return new Promise(function(resolve, reject) {
        var promises = [];

        for (var i = 0; i < rawJson.length; i++) {
            ...

            promises.push(new Promise(function(resolve) {
                ...
            }));
        }

        resolve(Promise.all(promises));
    });
}

现在,Promise.all(promises)将解析一系列结果。这可能不太理想,但如果您只是想等待使用它,那么您可以继续使用它:

return Promise.all(promises).then(function() {
    resolve(updatedJson);
}, reject);

通过这种方式,您可以让每个承诺修改响应数据。 requestNewData返回的承诺不会解决,直到所有这些承诺完成,因此此时updatedJson将会更新。

警告:Promise.all具有快速失败的行为。在你的情况下,我认为这正是你想要的。但是,如果您需要知道哪些失败,或者您需要等到所有请求完成(失败或其他),Promise.all可能不是正确的。

PS:如果reject功能提供request(),您应该error。否则,如果出现网络错误,您的数据可能会出现漏洞,而实际上并未获得拒绝。