首页 > 解决方案 > UnhandledPromiseRejectionWarning:NodeJs 中 _.map 中未处理的承诺拒绝

问题描述

当我运行下面的代码块时,我总是收到错误 - UnhandledPromiseRejectionWarning:未处理的承诺拒绝。此错误源于在没有 catch 块的情况下抛出异步函数内部或拒绝未使用 .catch() 处理的承诺。要在未处理的 Promise 拒绝时终止节点进程,请使用 CLI 标志--unhandled-rejections=strict(请参阅https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode)。(拒绝编号:1)


module.exports = (app, spotifyAPI) => {

    app.get('/api/search', requireLogin, async (req, res) => {

        const URI_BASE = keys.ComputerVisionEndpoint + 'vision/v3.0/analyze';
        const imageUrl = "https://upload.wikimedia.org/wikipedia/commons/3/3c/Shaki_waterfall.jpg"; // will be sent as req body
        var results;

        // making API call to microsoft cognitive services API 
        try {
            results = await axios({
                method: 'post',
                url: URI_BASE,
                headers: {
                    'Content-Type': 'application/json',
                    'Ocp-Apim-Subscription-Key' : keys.ComputerVision
                }, 
                params: {
                    'visualFeatures': 'Tags',
                    'details': '',
                    'language': 'en'
                },
                data: {
                "url": imageUrl,
                }
            });
        } catch (err) {
            return res.status(400).send(err);
        }

        // remove the common ones - indoor, outdoor, ground, wall, person, woman, man, ceiling, floor
        const to_filter = results['data']['tags'];
        _.remove(to_filter, (item) => {
            return (item.name === 'indoor' || item.name === 'outdoor' || item.name === 'ground' || item.name === 'wall'
                || item.name === 'person' || item.name === 'woman' || item.name === 'man' || item.name === 'ceiling'
                || item.name === 'floor'
            );
        });


        // searching for relevant songs and adding them to the playlist
        var id;
        try {
            id = await search_and_add(req, res, spotifyAPI, to_filter, playlist_id);
        } catch (err) {
            if (err['statusCode'] === 401) {
                req.logout();
                return res.redirect('/');
            }
            else {
                return res.status(400).send(err);
            }
        }

    });
}

search_and_add = async (req, res, spotifyAPI, to_filter, playlist_id) => {
    _.map(to_filter, async (tag) => {
        try {
            const song_details = await spotifyAPI.searchTracks(tag.name, { limit: 1 });
            //const song_uri = song_details['body']['tracks']['items'][0]['id'];
            console.log(song_details);
        } catch (err) {
            throw err;
        }
    });
     return;
    // figure out where to re direct user 
};

我很确定这是因为 search_and_add 函数中的 map 语句,但我不知道如何摆脱它并提供相同的功能来使 try-catch 块工作?有人可以帮忙吗?

标签: javascriptnode.jsexpressunhandled-exception

解决方案


  • 您正在使用 map 函数,因为您需要使用 Promise.allSettled 函数进行包装。

Promise.allSettled 在 node js 12 及以上版本中可用。如果您使用的节点数少于 12,那么您需要使用 Promise.all

Promise.allSettled():Promise.allSettled() 方法返回一个在所有给定的 Promise 都已实现或拒绝后解析的 Promise,并带有一组对象,每个对象都描述了每个 Promise 的结果。

它通常用于当您有多个不相互依赖的异步任务才能成功完成,或者您总是想知道每个承诺的结果。

Promise.all():Promise.all() 方法将一个可迭代的 Promise 作为输入,并返回一个 Promise 作为输出。当所有输入的承诺都已解决且非承诺已返回,或者输入可迭代不包含任何承诺时,此返回的承诺将解决。它会在任何输入的 promise 被拒绝或 non-promise 抛出错误时立即拒绝,并会在第一个拒绝消息/错误中拒绝。

它通常在有多个相互依赖才能成功完成的异步任务时使用,因为它不会等待并且会在任何输入承诺拒绝时立即拒绝。

参考这个:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

https://sung.codes/blog/2019/05/18/promise-race-vs-promise-any-and-promise-all-vs-promise-allsettled/

        const search_and_add = (req, res, spotifyAPI, to_filter, playlist_id) {
          return Promise.allSettled(to_filter.map(async (tag) => {
                const song_details = await spotifyAPI.searchTracks(tag.name, { limit: 1 });
                //const song_uri = song_details['body']['tracks']['items'][0]['id'];
                console.log(song_details);
               return song_details;
            }).catch(function(err){
               console.log(err);
              
              return err;
             });
        }

对于异步等待中的错误处理:

Async Await 本质上是 Promise 的语法糖,如果 await 语句出错,它将返回一个被拒绝的 Promise,而不是在每个地方添加 try -catch,我们可以编写一个帮助函数来包装我们的 express 路由来处理所有被拒绝的路由 Promise。

    const asyncMiddleware = fn =>
      (req, res, next) => {
        Promise.resolve(fn(req, res, next))
          .catch(next);
      };

然后像这样包装你的路由功能

    router.get('/users/:id', asyncMiddleware(async (req, res, next) => {
        /* 
          if there is an error thrown in getUserFromDb, asyncMiddleware
          will pass it to next() and express will handle the error;
        */
        const user = await getUserFromDb({ id: req.params.id })
        res.json(user);
    }));
    

注意:您也可以为此使用 npm 包async-middleware


推荐阅读