大家如何看待Nodejs中的Promise?

大家如何看待Nodejs中的Promise?

关于Promise的定义……可以参考Promises/A - CommonJS Wiki

现在npm上也有很多promise的库了,在下用的deferred(比较类似Q)。

首先谈谈在下的看法:

Promise是把原来靠异步回调取得的值做成一个返回值的形式,使这个值能够直接作用于上下文(上下文都支持Promise的情况下)来消解callback hell(最终只用一个callback来获取promise的结果)。

在下感觉这种“把同步传值的过程应用到异步调用中”的思路是很不错的, 但是Promise里面只有then这一种流控制,因此只能做既定的工作(?)比如把如下的异步调用代码(引用自You’re Missing the Point of Promises

getTweetsFor("domenic") // promise-returning async function
    .then(function (tweets) {
        var shortUrls = parseTweetsForUrls(tweets);
        var mostRecentShortUrl = shortUrls[0];
        return expandUrlUsingTwitterApi(mostRecentShortUrl); // promise-returning async function
    })
    .then(doHttpRequest) // promise-returning async function
    .then(
        function (responseBody) {
            console.log("Most recent link text:", responseBody);
        },
        function (error) {
            console.error("Error with the twitterverse:", error);
        }
    );

使用promise改写为

try {
    var tweets = getTweetsFor("domenic"); // blocking
    var shortUrls = parseTweetsForUrls(tweets);
    var mostRecentShortUrl = shortUrls[0];
    var responseBody = doHttpRequest(expandUrlUsingTwitterApi(mostRecentShortUrl)); // blocking x 2
    console.log("Most recent link text:", responseBody);
} catch (error) {
    console.error("Error with the twitterverse: ", error);
}

对于这种一步一步逐层回调往下执行的代码来说,Promise的实现是非常优雅的。可是对于需要通过某一步的计算结果来决定下一步的动作的代码来说(执行流里面出现了分支或者循环),又会出现嵌套的回调语句(因为需要取出值来判断,所以这里产成一个回调,然后回调里面又是新的一串promise操作……),这样一来当代码里面的分支/循环变多了以后还是会出现callback hell的状况……【不知道大家对于这种情况是如何解决的呢?

所以在下认为Promise这个模式还有可以改进的余地或者什么的……【又或者是在下的用法不对……


3 回复

标题:大家如何看待Node.js中的Promise?

内容: 大家好,今天想和大家分享一下我对Node.js中Promise的看法。首先,让我们回顾一下Promise的定义。根据CommonJS Wiki上的描述,Promise是一种用于处理异步操作的对象,它代表了一个还未完成但预期会完成的操作。

目前,在npm上有很多Promise库,例如我常用的deferred库(类似于Q)。首先,我想分享一下我对Promise的一些看法:

Promise的主要优点在于它将原本通过异步回调获取的值封装成了一个可以直接作用于上下文的形式。这意味着我们可以在多个函数之间传递这个值,并且在支持Promise的环境中,我们可以避免回调地狱(callback hell)的问题。Promise的核心方法是then,它允许我们在Promise对象的状态变为resolved或rejected时执行相应的操作。

例如,假设我们有一个异步操作getTweetsFor,它返回一个Promise对象。我们可以使用then方法来处理这个Promise的结果:

getTweetsFor("domenic")
    .then(function (tweets) {
        var shortUrls = parseTweetsForUrls(tweets);
        var mostRecentShortUrl = shortUrls[0];
        return expandUrlUsingTwitterApi(mostRecentShortUrl);
    })
    .then(doHttpRequest)
    .then(
        function (responseBody) {
            console.log("Most recent link text:", responseBody);
        },
        function (error) {
            console.error("Error with the twitterverse:", error);
        }
    );

然而,当代码中出现分支或循环时,Promise的链式调用可能会变得复杂。在这种情况下,我们仍然可能遇到嵌套的回调问题。为了更好地处理这种情况,可以考虑使用async/await语法,它使得异步代码看起来更像同步代码,从而提高了可读性和可维护性。

例如,上述代码可以重写为:

(async function () {
    try {
        const tweets = await getTweetsFor("domenic");
        const shortUrls = parseTweetsForUrls(tweets);
        const mostRecentShortUrl = shortUrls[0];
        const expandedUrl = await expandUrlUsingTwitterApi(mostRecentShortUrl);
        const responseBody = await doHttpRequest(expandedUrl);
        console.log("Most recent link text:", responseBody);
    } catch (error) {
        console.error("Error with the twitterverse: ", error);
    }
})();

通过这种方式,我们可以更清晰地表达逻辑流程,避免了嵌套的回调问题。

总之,Promise是一个强大的工具,可以帮助我们更好地管理异步代码。尽管它在某些情况下可能会显得复杂,但通过结合async/await语法,我们可以进一步提高代码的可读性和可维护性。希望我的分享对大家有所帮助!


promise这个东西感觉挺不错的 但是有一点promise是对函数的新封装, 调试起来麻烦而且浪费了很多不必要的性能 感觉还是JS的语法特性太少了

关于Node.js中的Promise,我认为它们是一种非常强大的工具,可以帮助我们避免回调地狱(Callback Hell)并更清晰地组织异步代码。然而,正如提问者提到的,当遇到需要根据前一步的计算结果来决定下一步执行逻辑的情况时,Promise的链式调用可能会变得复杂和难以管理。

示例代码

考虑一个场景,我们想要根据用户的输入选择不同的API请求。我们可以使用Promise来处理这种情况:

const axios = require('axios');

function getUserInput() {
    return new Promise((resolve, reject) => {
        // 假设这是用户输入
        resolve('domenic');
    });
}

function getTweetsFor(username) {
    return axios.get(`https://api.twitter.com/users/${username}/tweets`);
}

function parseTweetsForUrls(tweets) {
    return tweets.data.map(tweet => tweet.url);
}

function decideNextStep(shortUrls) {
    if (shortUrls.length > 0) {
        return expandUrlUsingTwitterApi(shortUrls[0]);
    } else {
        throw new Error('No URLs found in tweets.');
    }
}

function expandUrlUsingTwitterApi(shortUrl) {
    return axios.get(`https://api.twitter.com/expand?url=${shortUrl}`);
}

getUserInput()
    .then(getTweetsFor)
    .then(parseTweetsForUrls)
    .then(decideNextStep)
    .then(responseBody => {
        console.log("Most recent link text:", responseBody.data);
    })
    .catch(error => {
        console.error("Error with the twitterverse:", error.message);
    });

解释

  1. getUserInput: 模拟从用户获取输入。
  2. getTweetsFor: 发送请求获取用户推文。
  3. parseTweetsForUrls: 解析推文中包含的URL。
  4. decideNextStep: 根据解析结果决定下一步动作。
  5. expandUrlUsingTwitterApi: 扩展短网址。

通过这种方式,我们可以保持代码的结构化,并且在遇到分支逻辑时也可以清晰地处理。

总结

虽然Promise在处理简单的线性异步流程时非常方便,但在处理复杂的逻辑时可能需要更多的技巧。使用async/await语法可以使代码更易于理解和维护。

回到顶部