使用返回Promise的async函数:
async function doSomething(userId) {
return new Promise(async (resolve, reject) => {
const query = 'my-query';
const roomAggregate = Room.aggregate(query).allowDiskUse(true);
Room.aggregatePaginate(roomAggregate, async function (err, data) {
if (err || !data) {
throw err;
return {
success: false,
rooms: [],
};
} else {
const rooms = [];
for (let room of data.docs) {
const roomId = room._id;
room = await computeAThing(room, {
loadWriteUps: false,
loadCreators: false,
});
room.userCompleted = await computeBThing(userId, roomId);
rooms.push(room);
}
return resolve({
rooms,
success: true,
paginator: {
// some data related to pagination
},
});
}
});
});
}
我不确定它是否真的需要包含new Promise
,因为它已经被声明为async function
。在这种情况下是强制性的吗?
因为当这部分被移除,而在最后取代return resolve({...})
时,只有return {...}
,这似乎没有解决。
下面是修改后的代码,new Promise
和不同的返回:
async function doSomething(userId) {
const query = 'my-query';
const roomAggregate = Room.aggregate(query).allowDiskUse(true);
Room.aggregatePaginate(roomAggregate, async function (err, data) {
if (err || !data) {
throw err;
return {
success: false,
rooms: [],
};
} else {
const rooms = [];
for (let room of data.docs) {
const roomId = room._id;
room = await computeAThing(room, {
loadWriteUps: false,
loadCreators: false,
});
room.userCompleted = await computeBThing(userId, roomId);
rooms.push(room);
}
return {
rooms,
success: true,
paginator: {
// some data related to pagination
},
};
}
});
}
这个方法在其他地方被这样使用:
const myInfo = await someObj.doSomething('myUserId');
,然后检查:
if (myInfo.success) { ... }
对于第一种写法,它工作得很好,对于第二种写法,myInfo
是undefined
,它抛出一个错误,无法读取未定义的success
。
实现中缺少了什么吗?
对于第二个版本,我可以看到你实际上没有从doSomething
返回任何东西,所以我认为你应该这样做:
async function doSomething(userId) {
const query = 'my-query';
const roomAggregate = Room.aggregate(query).allowDiskUse(true);
const obj = Room.aggregatePaginate(roomAggregate, async function (err, data) {
if (err || !data) {
throw err;
return {
success: false,
rooms: [],
};
} else {
const rooms = [];
for (let room of data.docs) {
const roomId = room._id;
room = await computeAThing(room, {
loadWriteUps: false,
loadCreators: false,
});
room.userCompleted = await computeBThing(userId, roomId);
rooms.push(room);
}
return {
rooms,
success: true,
paginator: {
// some data related to pagination
},
};
}
});
return obj;
}
一般来说,你不需要在async函数中显式返回Promise,因为默认情况下,返回值将被包装在Promise中,不管它是什么。你只需要返回一些东西
Room.aggregatePaginat
是用延续传递的方式编写的,它不能很好地与承诺接口。通用的promisify
函数可用于将任何延续传递样式的函数转换为基于承诺的函数。如果您不想自己编写这个函数,Node提供了util.promisify
-
const promisify = f => (...args) =>
new Promise((resolve, reject) =>
f(...args, (err, data) => err ? reject(err) : resolve(data))
)
现在很容易重构你的doSomething
。注意:使用Promise.all
意味着所有rooms
数据都是并行处理的,而不是串行处理的-
async function doSomething(userId) {
const query = 'my-query'
const roomAggregate = Room.aggregate(query).allowDiskUse(true)
const {docs:rooms} = await promisify(Room.aggregatePaginate)(roomAggregate)
return {
rooms:
await Promise.all(rooms.map(async room => ({
...await computeAThing(room, {loadWriteUps: false, loadCreators: false}),
userCompleted: await computeBThing(userId, room._id)
}))),
success: true,
paginator: ...
}
}
你也应该避免像{ success: true }
这样的东西,因为一个已解决的承诺本质上是"成功的"。
注意return
出现在throw
之后。在这种情况下,return
是不可访问的,所以它不会做你认为它在做的事情。
if (err || !data) {
throw err;
return { // unreachable
success: false, // these four lines
rooms: [], // are completely ignored
}; // by javascript runtime
}
再说一次,{ success: false }
违背了Promise模式。如果您想从拒绝中恢复,并使用空{ rooms: [] }
列表恢复,请这样做-
doSomething(user.id)
.catch(err => ({ rooms: [] }))
.then(res => console.log(res.rooms))
更好的是,您可以在doSomething
中使用try..catch
,并在发生错误时返回相应的空响应。这可以防止错误冒泡并迫使用户处理它-
async function doSomething(userId) {
try { // try
const query = 'my-query'
const roomAggregate = Room.aggregate(query).allowDiskUse(true)
const {docs:rooms} = await promisify(Room.aggregatePaginate)(roomAggregate)
return {
rooms:
await Promise.all(rooms.map(async room => ({
...await computeAThing(room, {loadWriteUps: false, loadCreators: false}),
userCompleted: await computeBThing(userId, room._id)
}))),
paginator: ...
}
}
catch (err) { // catch
return { rooms: [] }
}
}
doSomething(user.id)
.then(res => console.log(res.rooms))