2016-12-20 21 views
6

Tôi đang sử dụng Node 7.2.1 với tính năng mới async/await. Tôi cũng đang sử dụng Native ES6 Promises với mongoose như thế này -Node.js UnhandledPromiseRejectionWarning ngay cả sau khi bắt được nó

const mongoose = require('mongoose'); 
mongoose.Promise = global.Promise; 

dòng mã của tôi là như thế này -

async function getFollowers(){ 
    try { 
     const followers = await User.getFollowersFromMongo(req.params.userId); 
     res.send(followers); 
    } catch (err) { 
     winston.error('Printing Error = ', err); 
     res.status(400).send({success: false, error: err}); 
    } 
} 

UserSchema.statics.getFollowersFromMongo = async(userId) => { 
    try { 
     let aggregateQuery = []; //some syntactical error in mongo query to produce exception 

     const followers = await User.aggregate(aggregateQuery); 
     return followers.map(follower => follower.followerData); 
    } 
    catch (err) { 
     return Promise.reject(err); 
    } 
}; 

Mã này hoạt động hoàn toàn tốt. Vấn đề phát sinh khi có một số lỗi được tạo ra. Vì vậy, tôi cố ý sửa đổi truy vấn mongoose của mình để MongoDB sẽ phát sinh lỗi.

Bây giờ MongoDB, như dự kiến ​​sẽ ném một lỗi hoàn toàn bị mã của tôi bắt và trả lại cho khách hàng với mã lỗi 400.

Vấn đề là mặc dù lỗi (cố ý) đã bị bắt bởi tôi, Node.js vẫn mang lại cho tôi cảnh báo này -

error: Printing Error = MongoError: path option to $unwind stage should be prefixed with a '$': followerData 
at Function.MongoError.create (/home/node_modules/mongodb-core/lib/error.js:31:11) 
at /home/node_modules/mongodb-core/lib/connection/pool.js:483:72 
at authenticateStragglers (/home/node_modules/mongodb-core/lib/connection/pool.js:429:16) 
at Connection.messageHandler (/home/node_modules/mongodb-core/lib/connection/pool.js:463:5) 
at Socket.<anonymous> (/home/node_modules/mongodb-core/lib/connection/connection.js:317:22) 
at emitOne (events.js:96:13) 
at Socket.emit (events.js:188:7) 
at readableAddChunk (_stream_readable.js:176:18) 
at Socket.Readable.push (_stream_readable.js:134:10) 
at TCP.onread (net.js:551:20) 

GET /user/385/followers 400 39.868 ms - 263 

(node:10158) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): MongoError: path option to $unwind stage should be prefixed with a '$': followerData 
(node:10158) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. 

Như có thể thấy yêu cầu của tôi đã trở lại 400 trạng thái và nhật ký lỗi của tôi cũng đã được in từ khối catch của phương thức ban đầu nhưng Node.js vẫn đang nói rằng thông báo lỗi không được xử lý.

Tại sao lại nói điều này ngay cả sau khi lỗi tương tự đã bị bắt?

Update - Nhờ @dvlsg và @Bergi, lỗi này đã được cố định trong phiên bản 4.7.5

+0

Tôi đoán sẽ là bởi vì bạn đang bắt nó với một try/catch thay vì .catch(). –

+1

@KevinB nhưng không phải là làm thế nào async/await xử lý lỗi? –

+0

liên quan: http://stackoverflow.com/questions/40500490/what-is-unhandled-promise-rejection –

Trả lời

2

Có chắc chắn dường như là một cái gì đó xa lạ với cách cầy mangut tổng hợp chơi với async/chờ đợi. Có vẻ như một con bọ, với tôi. Nếu có, nó chắc chắn nên được báo cáo cho mongoose.

Rất may, có một công việc dễ dàng xung quanh:

const followers = await User.aggregate(aggregateQuery).exec(); 

Thêm rõ ràng .exec() cho phép tôi để bắt lỗi đường ống tổng hợp như mong đợi.


Tôi nghĩ rằng vấn đề cơ bản thêm vào sự nhầm lẫn ở đây là có thêm Promise nổi xung quanh bị từ chối và không được xử lý. Bởi vì về mặt kỹ thuật, bạn xử lý việc từ chối dự kiến ​​ở đây chính xác. Nếu không, bạn sẽ không thấy rằng Printing error = ... đang được ghi nhật ký.

Đây là những gì tôi tin rằng đang xảy ra -

  • Bạn await User.aggregate()
  • Aggregate#then() được gọi thông qua await làm việc với thenables (tôi nghĩ)
  • Aggregate#exec() được gọi trong nội bộ bởi Aggregate#then()
  • Một mới Promise bên Aggregate#exec()created, và will be rejected
    • Đây là unhandled Promise, tôi tin.
  • Từ một callback được cung cấp cho Aggregate#exec() từ Aggregate#then(), các Error bên Aggregate#exec() sẽ provided to the callback
  • Bên trong callback trong Aggregate#then(), một mới createdPromise bị từ chối
    • Tôi tin Promise này được xử lý như dự kiến, vì đó là lợi nhuận từ Aggregate#then()

Tôi nghĩ rằng tôi có thể xác nhận sự nghi ngờ của mình bằng cách nhận xét ra this line trong mongoose Aggregate định nghĩa. Điều này sẽ ngăn chặn việc xử lý từ chối không được xử lý khỏi bị tấn công. Không phải là tôi đề nghị làm điều đó, nhân tiện. Đó chỉ là bằng chứng bổ sung, không phải là một giải pháp, kể từ bây giờ tôi chỉ có một không bị phản đối Promise nổi xung quanh.


Dưới đây là một cách tối thiểu-ish để tạo lại từ chối còn tự do trong một chút khép kín mã, để được chạy với node --harmony-async-await (thử nghiệm trên nút v7.2.1)

const mongoose = require('mongoose'); 
mongoose.Promise = global.Promise; 
mongoose.connect('mongodb://localhost/temp'); 

const userSchema = new mongoose.Schema({ 
    name: 'string' 
}); 

const User = mongoose.model('User', userSchema); 

async function run() { 
    try { 
    await User.aggregate([]); 
    } 
    catch (err) { 
    console.log('caught expected error:', err); 
    } 
} 

run(); 
+2

Nếu đây là trường hợp, nó chắc chắn nên được báo cáo cho mongoose như một lỗi. – Bergi

+1

Cảm ơn các liên kết đến nguồn, đó là điều tôi nghi ngờ. Sửa chữa nên thay đổi phương thức 'then' thành một hàm' đơn giản (onfulfill, onreject) {return this.exec(). Sau đó (onfulfill, onreject); } ', [tránh' antise pattern constructor 'Promise] (http://stackoverflow.com/q/23803743/1048572) mà không cần thiết tạo ra một lời hứa thứ hai thông qua gọi lại' exec'. Bạn có muốn mở vấn đề GH không? – Bergi

+0

Chắc chắn, tôi có thể làm điều đó. – dvlsg

Các vấn đề liên quan