【问题标题】:Understanding of promises in JavaScript理解 JavaScript 中的 Promise
【发布时间】:2014-05-21 17:10:34
【问题描述】:

我编写了相当多的 JavaScript 代码,虽然我认为我确实了解 Promise 的工作原理,但我不确定我是否完全理解 Promise 给 JS 世界带来的优势。考虑下面的代码,简单的异步调用,回调包含更多调用等等。

(function doWorkOldSchool() {

    setTimeout(function() {

        // once done resolve promise

        console.log("work done");

        setTimeout(function goHome() {

            // once done resolve promise

            console.log("got home");

            try {

                setTimeout(function cookDinner() {

                    // this exception will not be caught

                    throw "No ingredients for dinner!";
                    console.log("dinner cooked");

                    setTimeout(function goToSleep() {

                        // once done resolve promise

                        console.log("go to sleep");

                    }, 2000);

                }, 2000);

            } catch (ex) {
                console.log(ex);
            }

        }, 2000);

    }, 2000);

}());

我看到的一个问题:

  1. 在回调中抛出的异常是没有用的。是否正确地说,当 throw 调用发生时,这些 throw 调用超出范围,因此无法调用异常并且一直冒泡到顶部?如何处理这种异常?

  2. 第二个问题我发现这种嵌套业务可能会变得非常深入,即使您可以将回调函数代码保留在 setTimeout 代码之外,它也可能会变得一团糟。

那么首先有人可以澄清一下这种编码是否还有其他明显的问题或优势?

现在,我在下面准备了真正做同样事情的程序,但这次使用了 Promise:

function doWork() {

    return new Promise(function(res, rej) {

        // do your asynchronous stuff

        setTimeout(function() {

            // once done resolve promise

            res("work done");

        }, 2000);

    });
}


function goHome(succ) {

    console.log(succ);

    return new Promise(function(res, rej) {

        // do your asynchronous stuff

        setTimeout(function() {

            // once done resolve promise

            res("got home");

        }, 2000);

    });
}


function cookDinner(succ) {

    console.log(succ);

    //if exception thrown here it will be caught by chained err handler
    throw "No ingredients for dinner Exception!";

    return new Promise(function(res, rej) {

        // do your asynchronous stuff

        setTimeout(function() {

            // something went wrong so instead of using throw we reject the promise

            rej("No ingredients for dinner!");

            // once done resolve promise

        }, 2000);

    });
}


function goToSleep(succ) {

    console.log(succ);

    return new Promise(function(res, rej) {

        // do your asynchronous stuff

        setTimeout(function() {

            // once done resolve promise

            res("zzz... zz..");

        }, 2000);

    });
}


doWork()
    .then(goHome)
    .then(cookDinner)
    .then(goToSleep)
    .then(function(succ) {

        console.log(succ);

    }, function(err) {
        console.log(err);
    });

与以前的解决方案相比,我认为这种方法没有明显的问题,除了你显然必须了解编码/维护这个东西的承诺。然而,优点是:

  1. 在处理程序内部抛出的异常将被错误处理程序捕获,该处理程序被进一步链接到某个地方。

  2. 被拒绝的 Promise 将被链式错误处理程序捕获

  3. 代码更简洁

现在,我的理解是否正确,或者每种方法还有其他优点/缺点吗?

【问题讨论】:

  • 是的。是的。是的。我不会太担心后续的维护...... Promise 是 ES6 草案的一部分,并且有据可查。额外冗长的成本被顶级代码的可读性所抵消。

标签: javascript promise


【解决方案1】:

您的代码使用了一些反模式,您不应该在应用程序代码中创建承诺(例如,new Promise、“延迟”等)。您也绝不能将回调与 Promise 混为一谈,因为这样您就失去了异常冒泡(Promise 的意义)并使您的代码变得超级冗长。

您可以使用库或自己实现延迟:

function delay(ms, val) {
     return new Promise(function(res){setTimeout(res.bind(null, val), ms);});
}

然后:

function doWork() {
    return delay(2000, "work done");
}

doWork()
    .then(function(succ) {
        console.log(succ);
        return delay(2000, "got home");
    })
    .then(function(succ) {
        console.log(succ);
        // Never throw strings, yet another anti-pattern
        throw new Error("No ingredients for dinner Exception!");
        // Pointless to add code after throw
    })
    .then(function(succ) {
        return delay(2000, "zzz.. zz..");
    })
    // Don't use the second argument of .then, use .catch
    .catch(function(err) {
        console.log("error: ", err);
    });

为什么要使用.catch 而不是第二个参数?好吧,比较一下同步编写它的方式:

try {
    doWork();
    console.log("work done");
    sleep(2000);
    console.log("got home");
    sleep(2000);
    throw new Error("No ingredients for dinner Exception!";)
    sleep(2000);
    console.log("zzz.. zz..");
}
catch(e) {
    console.log("error: ", err);
}

明白了吗?

【讨论】:

  • 我不确定我是否理解您的第一段,请您详细说明或指向解释此内容的 URL 吗?
  • @spirytus 如果您查看我的代码并将其与您的代码进行比较,这不是解释吗? :P
【解决方案2】:

Promises 只能返回/决定一个值一次,因此一旦选择了一个值,它就无法更改。因此,如果用户单击 div 一次,Promise 只会执行一次
例如:

p = new Promise(function (res, rej) { 
var b = document.getElementById('helloWorld'); 
  b.onclick = function() {
    res(prompt('value'));
  }; 
});
p.then(function(val) { console.log(val); });

日志中始终只有一个值。 这对于游戏/应用程序的 GUI 和控件很有用。 你也可以在 Promise 中有两个事件监听器,这对于加载图像和文件很有用。即使您添加了成功或失败处理程序,Promise 也会做出反应,因此您创建一个成功的函数,尽管稍后创建失败函数,如果失败,正常事件处理程序会生成错误,尽管 Promise 不会并稍后调用该函数它被创建。这使您可以更多地关注如何对事件做出反应,而不必担心事情的时间安排。对于加载东西非常有用。
Amazing Page On Promises

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-16
    • 1970-01-01
    • 1970-01-01
    • 2020-06-27
    • 2015-08-15
    • 1970-01-01
    相关资源
    最近更新 更多