Firebase Cloud Functions通知功能:修复Promise嵌套与错误返回问题
Problem Overview
You're hitting two ESLint rule violations in your Firebase Cloud Functions notification code: promise nesting and missing return values in promise chains. These are causing your predeploy lint check to fail, and they also make your code harder to debug and maintain. Let's break down how to fix this.
First, here's your original code (with a small typo fixed for notification_id):
const functions = require('firebase-functions'); const admin= require('firebase-admin'); admin.initializeApp(); let firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG); exports.sendNotification = functions.database.ref('/Notifications/{user_id}/{notification_id}').onWrite((change,context) => { const user_id = context.params.user_id; const notification_id = context.params.notification_id; // Fixed typo console.log('We have a notification to send to ',user_id); if(!change.after.val()){ return console.log('A Notification has been deleted from the database : ', notification_id); } const deviceToken = admin.database().ref(`/Users/${user_id}/device_token`).once('value'); return deviceToken.then(result =>{ const token_id = result.val(); const payload = { notification: { title : "Friend Request", body : "You have received a new Friend Request", icon : "default" } }; return admin.messaging().sendToDevice(token_id, payload).then(response =>{ console.log("This was the notification Feature"); }); }); });
And your ESLint/deployment errors:
31:21 warning Avoid nesting promises promise/no-nesting 31:76 error Each then() should return a value or throw promise/always-return ✖ 2 problems (1 error, 1 warning) npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! functions@ lint: `eslint .` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the functions@ lint script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\furkan\AppData\Roaming\npm-cache\_logs\2018-05-27T20_21_53_984Z-debug.log Error: functions predeploy error: Command terminated with non-zero exit code 1
Solution: Refactor for Flat Promise Chains & Proper Returns
I'll show you two approaches: one using async/await (cleaner, modern syntax) and one using flat promise chains (if you prefer sticking with .then()).
Option 1: Async/Await (Recommended)
This eliminates promise nesting entirely and makes error handling straightforward with try/catch:
const functions = require('firebase-functions'); const admin = require('firebase-admin'); admin.initializeApp(); // Note: You probably don't need to parse FIREBASE_CONFIG unless you use it elsewhere // let firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG); exports.sendNotification = functions.database.ref('/Notifications/{user_id}/{notification_id}') .onWrite(async (change, context) => { const user_id = context.params.user_id; const notification_id = context.params.notification_id; console.log('We have a notification to send to ', user_id); // Handle notification deletion: return a resolved promise (don't just return console.log) if (!change.after.val()) { console.log('A Notification has been deleted from the database: ', notification_id); return Promise.resolve(); } try { // Fetch the user's device token const tokenSnapshot = await admin.database().ref(`/Users/${user_id}/device_token`).once('value'); const token_id = tokenSnapshot.val(); // Exit early if no token exists if (!token_id) { console.log('No device token found for user: ', user_id); return Promise.resolve(); } // Build notification payload const payload = { notification: { title: "Friend Request", body: "You have received a new Friend Request", icon: "default" } }; // Send notification and return the result to signal completion const response = await admin.messaging().sendToDevice(token_id, payload); console.log("Notification sent successfully:", response); return response; } catch (error) { // Log and re-throw errors so Firebase tracks them console.error("Error sending notification:", error); throw error; } });
Option 2: Flat Promise Chains
If you prefer to use .then() instead of async/await, we can flatten the chain and ensure every .then() returns a value:
const functions = require('firebase-functions'); const admin = require('firebase-admin'); admin.initializeApp(); exports.sendNotification = functions.database.ref('/Notifications/{user_id}/{notification_id}') .onWrite((change, context) => { const user_id = context.params.user_id; const notification_id = context.params.notification_id; console.log('We have a notification to send to ', user_id); if (!change.after.val()) { console.log('A Notification has been deleted from the database: ', notification_id); return Promise.resolve(); } // Flat chain instead of nesting .then() calls return admin.database().ref(`/Users/${user_id}/device_token`).once('value') .then(tokenSnapshot => { const token_id = tokenSnapshot.val(); if (!token_id) { console.log('No device token found for user: ', user_id); return Promise.resolve(); } const payload = { notification: { title: "Friend Request", body: "You have received a new Friend Request", icon: "default" } }; // Return the sendToDevice promise to continue the chain return admin.messaging().sendToDevice(token_id, payload); }) .then(response => { console.log("Notification sent successfully:", response); return response; // Return value to satisfy ESLint's always-return rule }) .catch(error => { console.error("Error sending notification:", error); throw error; // Propagate error to Firebase }); });
Key Fixes Explained:
- Eliminated Promise Nesting: In the original code, you nested a
.then()inside another.then()—we fixed this by chaining promises instead, resolving thepromise/no-nestingwarning. - Proper Return Values:
- For the deletion case, we return
Promise.resolve()instead of justconsole.log(Cloud Functions require a promise return to signal completion). - Every
.then()(orawaitcall) returns a value or promise, fixing thepromise/always-returnerror.
- For the deletion case, we return
- Added Error Handling: We added
try/catch(for async/await) or.catch()(for chains) to log and propagate errors, so Firebase can track failures properly. - Token Check: We now handle the case where a user doesn't have a device token stored, avoiding unnecessary API calls.
- Fixed Typo: Corrected the variable name from
notificationtonotification_idin the deletion log.
内容的提问来源于stack exchange,提问作者f.knyr

