我已经开发了几年的JavaScript,我根本不理解有关承诺的大惊小怪 .
似乎我所做的就是改变:
api(function(result){
api2(function(result2){
api3(function(result3){
// do work
});
});
});
无论如何,我可以使用像async这样的库,例如:
api().then(function(result){
api2().then(function(result2){
api3().then(function(result3){
// do work
});
});
});
哪个代码更多,可读性更低 . 我没有在这里获得任何东西,它也不会突然神奇地“平坦” . 更不用说必须将事物转换为承诺 .
那么,这里的承诺有什么大惊小怪的呢?
7 回答
承诺不是回调 . 承诺代表 future result of an asynchronous operation . 当然,按照你的方式写它们,你得到的好处不大 . 但是如果按照它们的使用方式编写它们,您可以以类似于同步代码的方式编写异步代码,并且更容易遵循:
当然,代码不多,但更具可读性 .
但这不是结束 . 让我们发现真正的好处:如果您想检查任何步骤中的任何错误怎么办?用回调来做这件事就好了,但是有了承诺,这是小菜一碟:
与
try { ... } catch
块几乎相同 .更好的是:
甚至更好:如果那些对
api
,api2
,_568120的3次调用可以同时运行(例如,如果它们是AJAX调用)但你需要等待这三个呢?没有承诺,你应该创造某种反击 . 承诺,使用ES6符号,是另一块蛋糕,非常整洁:希望你现在以新的眼光看待Promise .
是的,Promises是异步回调 . 他们不能做回调不能做的任何事情,并且你遇到与异步回调相同的异步问题 .
然而,Promise不仅仅是回调 . 它们是一个非常强大的抽象,允许更清晰,更好,功能更强的代码,并且更容易出错 .
Promise是表示单个(异步)计算结果的对象 . 他们只有一次resolve to that result . 这意味着什么:
Promise实现了一个观察者模式:
在任务完成之前,您不需要知道将使用该值的回调 .
而不是期望回调作为函数的参数,你可以轻松
return
一个Promise对象promise将存储该值,您可以随时透明地添加回调 . 结果可用时将调用它 . "Transparency"意味着当你有一个承诺并添加一个回调时,它对你的代码没有影响,无论结果是否已经到来 - API和 Contract 是相同的,简化了缓存/备忘 .
您可以轻松添加多个回调
Promises are chainable(monadic,如果你想):
如果需要转换promise所代表的值,则将转换函数映射到promise并返回表示转换结果的新promise . 您无法以某种方式同步获取值以使用它,但您可以轻松地在promise上下文中解除转换 . 没有样板回调 .
如果要链接两个异步任务,可以使用
.then()
方法 . 它将使用第一个结果调用回调,并返回回调返回的承诺结果的承诺 .听起来很复杂?代码示例的时间 .
展平不是神奇的,但你可以很容易地做到 . 对于重度嵌套的示例,(近)等价物将是
如果看到这些方法的代码有助于理解,here's a most basic promise lib in a few lines .
Promise抽象允许更好的函数可组合性 . 例如,在链接的
then
旁边,all
函数为多个并行等待的promise的组合结果创建一个promise .最后但并非最不重要的Promise带有集成的错误处理 . 计算的结果可能是承诺是用值来实现的,或者是因为理由而被拒绝 . 所有组合函数都自动处理这个问题,并在promise链中传播错误,因此您无需在任何地方明确地关注它 - 与普通回调实现相反 . 最后,您可以为所有发生的异常添加专用错误回调 .
实际上有很好的承诺库,这是非常微不足道的,请参阅How do I convert an existing callback API to promises?
除了已经 Build 的答案之外,凭借ES6箭头功能,Promise从一颗谦虚闪亮的小蓝矮星直接转变为红色巨人 . 那即将崩溃成超新星:
正如oligofren指出的那样,在api调用之间没有参数,你根本不需要匿名包装器函数:
最后,如果你想达到一个超大质量的黑洞水平,可以期待Promises:
除了其他答案之外,ES2015语法与承诺无缝融合,进一步降低样板代码:
除了上面的精彩答案,还可以添加2个点:
1. Semantic difference:
承诺可能在创建时已经解决 . 这意味着他们保证条件而不是事件 . 如果它们已经被解析,则仍然会调用传递给它的已解析函数 .
相反,回调处理事件 . 因此,如果您感兴趣的事件发生在回调注册之前,则不会调用回调 .
2. Inversion of control
回调涉及控制的倒置 . 当您使用任何API注册回调函数时,Javascript运行时会存储回调函数,并在准备好运行后从事件循环中调用它 .
有关说明,请参阅The Javascript Event loop .
使用Promises,控制驻留在调用程序中 . 如果我们存储promise对象,则可以随时调用.then()方法 .
Promise不是回调,两者都是促进异步编程的编程习惯 . 使用返回promises的协同程序或生成器的async / await-style编程可以被认为是第三种这样的习惯用法 . 这些成语在不同编程语言(包括Javascript)中的比较如下:https://github.com/KjellSchubert/promise-future-task
没有承诺只是回调的包装
示例您可以将javascript本机承诺与节点js一起使用