【发布时间】:2022-01-19 03:06:07
【问题描述】:
我在 Node.js 中有一组函数,我想按特定顺序加载它们。我将提供一些抽象和简化的模型代码:
function updateMyApp() {
loadDataToServer()
.then(() => useData())
.then(() => saveData())
.then(() => { console.log("updateMyApp done") })
}
function loadDataToServer() {
return new Promise( (resolve, reject) {
...preparing data and save file to cloud...
resolve()})
}
function handleDataItem(item) {
// Function that fetches data item from database and updates each data item
console.log("Name", item.name)
}
function saveData() {
// Saves the altered data to some place
}
useData 有点复杂。在里面我想,按顺序:
- console.log('正在启动 alterData()')
- 从云数据源以 json 格式加载数据
- 遍历 json 文件中的每一项并对其执行 handleDataItem(item)。
- 当 #2 完成时 -> console.log('alterData() done')
- 将已解决的承诺返回给 updateMyApp
- 继续使用
saveData()更改所有数据。
我希望显示日志:
Starting useData()
Name: Adam
Name: Ben
Name: Casey
useData() done
我对此的看法如下:
function useData() {
console.log('Starting useData()')
return new Promise( function(resolve, reject) {
readFromCloudFileserver()
.then(jsonListFromCloud) => {
jsonListFromCloud.forEach((item) => {
handleDataItem(item)
}
})
.then(() => {
resolve() // I put resolve here because it is not until everything is finished above that this function is finished
console.log('useData() done')
}).catch((error) => { console.error(error.message) })
})
}
这似乎可行,但据我了解,这不是一个人应该这样做的方式。此外,这似乎在此链之外执行handleDataItem,因此日志如下所示:
Starting useData()
useData() done
Name: Adam
Name: Ben
Name: Casey
换句话说。当链移动到下一步 (.then()) 时,handleDataItem() 调用似乎没有完成。换句话说,我不能确定当它继续到saveData() 函数时所有项目都已更新?
如果这不是一个很好的处理方式,那么这些函数应该怎么写呢?如何正确链接函数以确保一切都以正确的顺序完成(以及使日志事件按顺序显示)?
编辑:根据要求,这对 handleDataItem 的抽象程度较低。
function handleDataItem(data) {
return new Promise( async function (resolve) {
data['member'] = true
if (data['twitter']) {
const cleanedUsername = twitterApi.cleanUsername(data['twitter']).toLowerCase()
if (!data['twitter_numeric']) {
var twitterId = await twitterApi.getTwitterIdFromUsername(cleanedUsername)
if (twitterId) {
data['twitter_numeric'] = twitterId
}
}
if (data['twitter_numeric']) {
if (data['twitter_protected'] != undefined) {
var twitterInfo = await twitterApi.getTwitterGeneralInfoToDb(data['twitter_numeric'])
data['twitter_description'] = twitterInfo.description
data['twitter_protected'] = twitterInfo.protected
data['twitter_profile_pic'] = twitterInfo.profile_image_url.replace("_normal", '_bigger')
data['twitter_status'] = 2
console.log("Tweeter: ", data)
}
} else {
data['twitter_status'] = 1
}
}
resolve(data)
}).then( (data) => {
db.collection('people').doc(data.marker).set(data)
db.collection('people').doc(data.marker).collection('positions').doc(data['report_at']).set(
{
"lat":data['lat'],
"lon":data['lon'],
}
)
}).catch( (error) => { console.log(error) })
}
调用的 twitterAPI 函数:
cleanUsername: function (givenUsername) {
return givenUsername.split('/').pop().replace('@', '').replace('#', '').split(" ").join("").split("?")[0].trim().toLowerCase()
},
getTwitterGeneralInfoToDb: async function (twitter_id) {
var endpointURL = "https://api.twitter.com/2/users/" + twitter_id
var params = {
"user.fields": "name,description,profile_image_url,protected"
}
// this is the HTTP header that adds bearer token authentication
return new Promise( (resolve,reject) => {
needle('get', endpointURL, params, {
headers: {
"User-Agent": "v2UserLookupJS",
"authorization": `Bearer ${TWITTER_TOKEN}`
}
}).then( (res) => {
console.log("result.body", res.body);
if (res.body['errors']) {
if (res.body['errors'][0]['title'] == undefined) {
reject("Twitter API returns undefined error for :'", cleanUsername, "'")
} else {
reject("Twitter API returns error:", res.body['errors'][0]['title'], res.body['errors'][0]['detail'])
}
} else {
resolve(res.body.data)
}
}).catch( (error) => { console.error(error.message) })
})
},
// Get unique id from Twitter user
// Twitter API
getTwitterIdFromUsername: async function (cleanUsername) {
const endpointURL = "https://api.twitter.com/2/users/by?usernames="
const params = {
usernames: cleanUsername, // Edit usernames to look up
}
// this is the HTTP header that adds bearer token authentication
const res = await needle('get', endpointURL, params, {
headers: {
"User-Agent": "v2UserLookupJS",
"authorization": `Bearer ${TWITTER_TOKEN}`
}
})
if (res.body['errors']) {
if (res.body['errors'][0]) {
if (res.body['errors'][0]['title'] == undefined) {
console.error("Twitter API returns undefined error for :'", cleanUsername, "'")
} else {
console.error("Twitter API returns error:", res.body['errors'][0]['title'], res.body['errors'][0]['detail'])
}
} else {
console.error("Twitter API special error:", res.body)
}
} else {
if (res.body['data']) {
return res.body['data'][0].id
} else {
//console.log("??? Could not return ID, despite no error. See: ", res.body)
}
}
},
【问题讨论】:
-
handleDataItem是异步的吗?它会返回一个承诺吗? -
我们需要查看
handleDataItem()的代码。由于它显然是异步的(更新您的数据库)并且在您看到问题的地方,这可能是整个问题的很大一部分。我们需要帮助您修复该代码。 -
仍在尝试从头到尾遵循代码。你用的是什么数据库?
db.collection('people').doc(data.marker).set(data)是否返回承诺? -
我正在使用 Google Cloud Firestore (nosql)。这行代码不返回承诺。我已经开始使用 try/catch 来实施您的解决方案,而且它似乎工作得更好。目前看来,这可能是我的问题的解决方案,但我需要更深入地研究 Promises,因为我真的不太明白。