初识Promises[通俗易懂]

初识Promises[通俗易懂]原文:PromisesinNode.jswithQ–AnAlternativetoCallbacksby MarcHarter 《Node.jsinPractice》怎么写异步代码?相对原始的callbacks而言,promises无疑是更好的选择。可掌握promises的概念及其用法可能不太容易,而且很有可能你已经放弃它了。但经过一大波码农的努力,promi

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

原文:Promises in Node.js with Q – An Alternative to Callbacks
by Marc Harter 《Node.js in Practice》

怎么写异步代码?相对原始的callbacks而言,promises无疑是更好的选择。可掌握promises的概念及其用法可能不太容易, 而且很有可能你已经放弃它了。但经过一大波码农的努力,promise的美终于以一种可互操、可验证的方式现于世间。这一努力的结果就是Promises/A+规范,它以自己的方式影响了各种promises库,甚至DOM。

扯了这么多,promises到底是什么?写Node程序时它能帮上什么忙?

Promises是一个。。。抽象

我们先来聊聊promise的行为模式,让你对他是什么,能怎么用他有个直观的感受。在本文的后半段,我们会以Q为例讲一下在程序里怎么创建和使用promise。

那promise究竟是什么呢?请看定义:

promise是对异步编程的一种抽象。它是一个代理对象,代表一个必须进行异步处理的函数返回的值或抛出的异常。 – Kris Kowal on JSJ

callback是编写Javascript异步代码最最最简单的机制。可用这种原始的callback必须以牺牲控制流、异常处理和函数语义为代价,而我们在同步代码中已经习惯了它们的存在,不适应!Promises能带它们回来。

promise对象的核心部件是它的then方法。我们可以用这个方法从异步操作中得到返回值(传说中的履约值),或抛出的异常(传说中的拒绝的理由)。then方法有两个可选的参数,都是callback函数,分别是onFulfilled 和 onRejected

var promise = doSomethingAync() promise.then(onFulfilled, onRejected)

Jetbrains全家桶1年46,售后保障稳定

promise被解决时(异步处理已经完成)会调用onFulfilled 和 onRejected 。因为只会有一种结果,所以这两个函数中仅有一个会被触发。

从Callbacks 到 promises

看过这个promises的基础知识后,我们再来看一个经典的异步 Node callback:

readFile(function (err, data) { 
    if (err) return console.error(err) console.log(data) })

如果函数readFile返回的是 promise,我们可以这样写:

var promise = readFile() promise.then(console.log, console.error)

乍一看这没什么实质性变化。但实际上现在我们得到了一个代表异步操作的值(promise)。我们可以传递promise,不管异步操作完成与否,所有能访问到promise的代码都可以用then使用这个异步操作的处理结果。而且我们还得到保证,异步操作的结果不会发生某种变化,因为promise只会被解决一次(或履约,或被拒)。

then当成对promise解包以得到异步操作结果(或异常)的函数对理解promise更有帮助,不要把它当成只是带两个callback(onFulfilled 和 onRejected)的普通函数。详情请见此文

promise的链接及内嵌

then方法返回的还是promise。

var promise = readFile() var promise2 = promise.then(readAnotherFile, console.error)

这个promise表示 onFulfilled 或 onRejected 的返回结果。既然结果只能是其中之一,所以不管是什么结果,promise都会转发调用:

var promise = readFile() var promise2 = promise.then(function (data) { 
    return readAnotherFile() // if readFile was successful, let's readAnotherFile }, function (err) { 
    console.error(err) // if readFile was unsuccessful, let's log it but still readAnotherFile return readAnotherFile() }) promise2.then(console.log, console.error) // the result of readAnotherFile

因为then 返回的是 promise,所以promise可以形成调用链,避免出现callback大坑

readFile() .then(readAnotherFile) .then(doSomethingElse) .then(...)

如果非要保持闭包,promise也可以嵌套:

readFile() .then(function (data) { 
    return readAnotherFile().then(function () { 
    // do something with `data` }) })

Promise与同步函数

Promises有几种编写同步函数的办法。其中之一是用返回代替调用。在前面的例子中,返回readAnotherFile()是一个信号,表明在readFile完成之后做什么。

如果返回promise,它会在异步操作完成后发信号给下一个then。返回值并不是非promise不可,不管返回什么,都会传给下一个onFulfilled做参数:

readFile() .then(function (buf) { 
    return JSON.parse(buf.toString()) }) .then(function (data) { 
    // do something with `data` })

promise的错误处理

除了return,还可以用关键字throw 和 try/catch语法。这可以算是promises最强的一个特性了。下面我们来看一段同步代码:

try { 
    doThis() doThat() } catch (err) { 
    console.error(err) }

在上例中,如果doThis() 或 doThat()抛出了异常,异常会被捕获并输出错误日志。既然try/catch允许多个操作放到一起,我们就不用单独处理每个操作可能出现的错误。用promises的异步代码也可以这样:

doThisAsync() .then(doThatAsync) .then(null, console.error)

如果doThisAsync()没有成功,它的promise会被拒,处理链下一个then上的onRejected会被调用。在上例中就是函数console.error。而且跟 try/catch 一样, doThatAsync() 根本就不会被调用。对于原始的callback那种每一步里都要显式处理错误的方式而言,这是巨大的进步。

实际上它比这还要好!任何被抛出的异常,隐式的或显式的,then的回调函数中的也会处理:

doThisAsync() .then(function (data) { 
    data.foo.baz = 'bar' // throws a ReferenceError as foo is not defined }) .then(null, console.error)

上例中抛出的ReferenceError会被处理链中下一个onRejected捕获。相当漂亮!当然,这对显式抛出的异常也有效:

doThisAsync() .then(function (data) { 
    if (!data.baz) throw new Error('Expected baz to be there') }) .then(null, console.error)

对错误处理的重要提示

我们在前面已经说过了,promises模拟了try/catch。在try/catch中,可以不对异常做显式的处理,屏蔽它:

try { 
    throw new Error('never will know this happened') } catch (e) {}

对promise来说也是如此:

readFile() .then(function (data) { 
    throw new Error('never will know this happened') })

要处理被屏蔽的错误,可以在promise处理链的最后加一个.then(null, onRejected):

readFile() .then(function (data) { 
    throw new Error('now I know this happened') }) .then(null, console.error)

各种函数库中还包括暴露被屏蔽错误的其他选项。比如Q中的done方法可以重新向上抛出错误。

promise的具体应用

前面的例子都是返回空方法,只是为了阐明Promises/A+中的then 方法。接下来我们要看一些更具体的例子。

将callbacks 变成 promises

你可能在想promise最初是从哪蹦出来的。Promise/A+规范中没有规定创建promise的API,因为它不会影响互操作性。因此不同promise库的实现可能是不同的。我们的例子用的是Q(npm install q).

Node 核心异步函数不会返回promises;它们采用了callbacks的方式。然而用Q可以很容易地让它们返回promises:

var fs_readFile = Q.denodify(fs.readFile) var promise = fs_readFile('myfile.txt') promise.then(console.log, console.error)

Q 提供了一些辅助函数,可以将Node和其他环境适配为promise可用的。请参见 readme 和API documentation 了解详情。

创建原始的promise

Q.defer可以手动创建promise。比如将fs.readFile手工封装成promise的(基本上就是Q.denodify做的事情 )

function fs_readFile (file, encoding) { 
    var deferred = Q.defer() fs.readFile(file, encoding, function (err, data) { 
    if (err) deferred.reject(err) // rejects the promise with `er` as the reason else deferred.resolve(data) // fulfills the promise with `data` as the value }) return deferred.promise // the promise is returned } fs_readFile('myfile.txt').then(console.log, console.error)

做同时支持callbacks 和 promises 的APIs

我们已经见过两种将callback代码变成promise代码的办法了。其实还能做出同时提供promise和callback接口的APIs。下面我们就把fs.readFile变成这样的API:

function fs_readFile (file, encoding, callback) { 
    var deferred = Q.defer() fs.readFile(function (err, data) { 
    if (err) deferred.reject(err) // rejects the promise with `er` as the reason else deferred.resolve(data) // fulfills the promise with `data` as the value }) return deferred.promise.nodeify(callback) // the promise is returned }

如果提供了callback,当promise被拒或被解决时,会用标准Node风格的(err, result) 参数调用它。

fs_readFile('myfile.txt', 'utf8', function (er, data) { 
    // ... })

用promise执行并行操作

我们前面聊的都是顺序的异步操作。对于并行操作,Q提供了Q.all方法,它以一个promises数组作为输入,返回一个新的promise。 在数组中的所有操作都成功完成后,这个promise就会履约。如果任何一个操作失败,这个新的promise就会被拒。

var allPromise = Q.all([ fs_readFile('file1.txt'), fs_readFile('file2.txt') ]) allPromise.then(console.log, console.error)

不得不强调一下,promise在模仿函数。函数只有一个返回值。当传给Q.all两个成功完成的promises时,调用onFulfilled只会有一个参数(一个包含两个结果的数组)。你可能会对此感到吃惊;然而跟同步保持一致是promise的一个重要保证。如果你想把结果展开成多个参数,可以用Q.spread

让promise更具体

要想真正理解promise,最好的办法就是用一用。下面是几个帮你开始的主意:

  1. 封装一些基本的Node流程,将callbacks 变成 promises
  2. 重写一个async方法,变成使用promise的
  3. 写一些递归使用promises的东西(目录树应该是个不错的开端)
  4. 写一个过得去的 Promise A+实现
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/230929.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • java代码生成器,springboot代码生成器—增加更新,查询功能(持续更新)

    java代码生成器,springboot代码生成器—增加更新,查询功能(持续更新)时隔一周多,今天终于抽出时间来更新一波代码生成器,最近公司让我研究rpa,弄得焦头烂额的,话不多说,进入正题。之前有朋友让我讲一下代码生成器的原理,这篇博客就大体描述一下,以后慢慢细致讲解。双击codeMan.exe,众所周知,java做成exe程序很麻烦,在这里我是利用了.net的ikvm插件把jar包直接编译成了exe程序,这个启动界面会连接我的服务器,去检查版本更新,如果有更新就会在…

  • postgreSQL + Drupal 安装

    postgreSQL + Drupal 安装最近在做毕业论文,需要用到Linux(Fedora)下的postgreSQL+Drupal构建。就这么边学边做,碰到许多小问题,折腾了好几天,终于把网站架起来。一些问题的解决办法:1、用yum安装postgreSQL算是简单的,但是按某教程装好后直接运行servicepostgresqlstart总是不成功。后来查其他资料,发现在第一次start之前需要执行servicepost

  • opencv跟踪视频上的目标(理论分析框架)

    出处:http://hi.baidu.com/icekeydnet/blog/item/965b25154a19f3dea6ef3ffe.html如前面说到的,OpenCVVS提供了6组算法的接口,分别是:前景检测、新目标检测、目标跟踪、轨迹生成、跟踪后处理、轨迹分析,除了轨迹生成用于轨迹数据的保存以外,其他5个部分都是标准的视频监控算法体系中不可或缺的部分。      OpenC

  • 8.WLAN频段介绍_频段与信道「建议收藏」

    8.WLAN频段介绍_频段与信道「建议收藏」频段与信道1、ISM频段一、pandas是什么?二、使用步骤1.引入库2.读入数据总结1、ISM频段一、pandas是什么?示例:pandas是基于NumPy的一种工具,该工具是为了解决数据分析任务而创建的。二、使用步骤1.引入库代码如下(示例):importnumpyasnpimportpandasaspdimportmatplotlib.pyplotaspltimportseabornassnsimportwarningswarnings.fil

  • 静态代理理解_如何做代理

    静态代理理解_如何做代理静态代理现在如果要对每个..

    2022年10月16日
  • curl 要么 file_get_contents 获得授权页面的方法的必要性

    curl 要么 file_get_contents 获得授权页面的方法的必要性

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号