promise then err_我们所了解的Promise

了解为什么以及如何在Node.js中使用promises

5c8e686e-8501-443e-b3fc-2e3dadfa9a27

回调是在JavaScript中处理异步代码的最简单的机制。 然而,原始回调牺牲了开发人员在同步代码中熟悉的控制流,异常处理和函数语义:

// Asynchronous operations return no meaningful valuevar noValue = fs.readFile('file1.txt', function(err, buf) { // Errors are explicitly handled every time if (err) return handleErr(err) fs.readFile('file2.txt', function(err2, buf2) { if (err2) return handleErr(err2) data.foo.baz = 'bar' // Exceptions like this ReferenceError are not caught // Sequential operations encourage heavy nesting fs.readFile('file3.txt', function(err3, buf3) { if (err3) return handleErr(err3) }) }) })

Promise提供了一种方法来恢复控制:

更强大的控制流程

更好的异常处理

函数式编程语义

尽管如此,Promise可能会令人困惑,因此你可能已经将它们写下来或直接跳过async / await,这为JavaScript提供了新的Promise语法。

但是,了解Promise如何在基本层面上发挥作用和行为将有助于您充分利用它们。 在本文中,我们将介绍Promise的基础知识,包括它是什么,如何创建它,以及如何最有效地使用它。

抽像的Promise

首先,让我们来看看Promise的行为:它是什么以及它如何有用? 然后我们将讨论如何创建和使用Promise。

什么是Promise? 我们来看一个定义:

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

Promise对象的核心组件是它的then方法。 then方法是如何从异步操作中获取返回值(称为实现值)或抛出异常(称为拒绝原因)。 然后将两个可选的回调作为参数,我们将调用onFulfilled和onRejected:

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

当Promise解析时(异步处理已完成),onFulfilled和onRejected触发器。 其中一个功能将触发,因为只有一个是可能的。

Promise回调

鉴于这些Promise的基本知识,让我们看一下熟悉的异步Node.js回调:

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

如果我们的readFile函数返回了一个promise,我们将编写相同的逻辑:

let promise = readFile()promise.then(console.log, consoler.error)

乍一看,它似乎改变了美学。 但是,我们现在可以访问表示异步操作(promise)的值。 我们可以在代码中传递Promise,就像JavaScript中的任何其他值一样。 无论异步操作是否已完成,任何有权访问promise的人都可以使用它。 我们还保证异步操作的结果不会以某种方式改变,因为promise将resolve一次(即fulfilled 或 rejected)。

将其视为一个需要两个回调函数(onFulfilled和onRejected)的函数是有帮助的,但是作为一个函数,它展开了揭示异步操作发生的情况。 任何有权访问权限的人都可以使用它来打开它。

Promise的链接与嵌套

then方法返回Promise

let promise = readFile()let promise2 = promise.then(readAnotherFile, console.error)
ab5b65bd-c5e6-49d3-8a18-0b292475e31f

此promise表示其onFulfilled或onRejected处理程序的返回值(如果已指定),promise将代理触发处理程序:

let promise = readFile()let 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可以链接在一起,以避免回调地狱的深层嵌套:

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

Promse 与同步函数

Promise可以模拟同步功能。 使用return来继续而不是调用另一个函数。 前面的示例返回readAnotherFile()以表示在readFile()之后要执行的操作。

如果您返回一个promise,它将在异步操作完成时发出下一个信号。 您还可以返回任何其他值,下一个onFulfilled将获取该值作为参数:

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

Promise的异常处理

您还可以使用throw关键字以及try / catch。 这可能是Promise最强大的功能之一。 例如,请考虑以下同步代码:

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

在这个例子中,如果doThis()或doThat()会抛出错误,我们将捕获并记录错误。 由于try / catch块允许分组操作,因此我们可以避免显式处理每个操作的错误。 我们可以使用promises异步执行同样的操作:

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

如果doThisAsync()不成功,则其promise将拒绝,然后在链中使用onRejected处理程序触发。 在这种情况下,它是console.error函数。 和try / catch块一样,doThatAsync()永远不会被调用。 这是对原始回调的改进,您必须在每个步骤中明确处理错误。

但是,任何抛出的异常,无论是隐式还是显式的 - 其回调也在promises中处理:

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

这里,引发的ReferenceError触发链中的下一个onRejected处理程序。 很简单! 当然,这也适用于显式抛出:

doThisAsync() .then(function(data) { if (!data.baz) throw new Error('Expected baz to be there') }) .catch(console.error) // The catch(fn) is shorthand for .then(undefined, fn)

错误处理的重要说明

如前所述,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')})

要公开掩码错误,解决方案是使用简单的.catch(onRejected)子句结束promise链:

readFile() .then(function(data) { throw new Error('Now I know this happened') }) .catch(console.error)

构造Promise

您也可以使用Promise构造函数创建一个promise。 让我们转换相同的fs.readFile方法来返回promises而不使用util.promisify:

const fs = require('fs')function readFile(file, encoding) { return new Promise(function(resolve, reject) { fs.readFile(file, encoding, function(err, data) { if (err) return reject(err) // Rejects the promise with `err` as the reason resolve(data) // Fulfills the promise with `data` as the value }) })}let promise = readFile('myfile.txt')promise.then(console.log, console.error)

创建支持回调和Promise的API

我们已经看到了两种将回调代码转换为promise代码的方法。 您还可以创建提供promise和回调接口的API。 例如,让我们将fs.readFile转换为支持回调和承诺的API:

const fs = require('fs')function readFile(file, encoding, callback) { if (callback) return fs.readFile(file, encoding, callback) // Use callback if provided return new Promise(function(resolve, reject) { fs.readFile(file, encoding, function(err, data) { if (err) return reject(err) resolve(data) }) })}

如果存在回调,则使用标准Node样式(错误,结果)参数触发它。

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

使用promises进行并行操作

我们已经讨论过顺序异步操作。 对于并行操作,ES6 / 2015提供了Promise.all方法,该方法接受一系列promise并返回一个新的promise。 所有操作完成后,新Promise即告完成。 如果任何操作失败,则返回reject promise。

let allPromise = Promise.all([readFile('file1.txt'), readFile('file2.txt')])allPromise.then(console.log, console.error)
4005bc74-f747-49da-b581-e6796add98ce

总结

理解承诺的最好方法是使用它们。 以下是一些让您入门的建议:

包装一些标准的Node.js库函数,将回调转换为promises。 没有使用node.promisify实用程序作弊!

使用async / await获取函数并重写它而不使用该语法糖。 这意味着您将返回一个promise并使用then方法。

使用promises递归写一些东西(目录树将是一个好的开始)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值