Promise的概念怎么理解,用法具体是什么
Admin 2022-07-14 群英技术资讯 292 次浏览
异步是为了提高CPU的占用率,让其始终处于忙碌状态。
有些操作(最典型的就是I/O)本身不需要CPU参与,而且非常耗时,如果不使用异步就会形成阻塞状态,CPU空转,页面卡死。
在异步环境下发生I/O操作,CPU就把I/O工作扔一边(此时I/O由其他控制器接手,仍然在数据传输),然后处理下一个任务,等I/O操作完成后通知CPU(回调就是一种通知方式)回来干活。
《JavaScript异步与回调》想要表达的核心内容是,异步工作的具体结束时间是不确定的,为了准确的在异步工作完成后进行后继的处理,就需要向异步函数中传入一个回调,从而在完成工作后继续下面的任务。
虽然回调可以非常简单的实现异步,但是却会由于多重嵌套形成回调地狱。避免回调地狱就需要解嵌套,将嵌套编程改为线性编程。
Promise
是JavaScript
中处理回调地狱最优解法。
Promise
可以翻译为“承诺”,我们可以通过把异步工作封装称一个Promise
,也就是做出一个承诺,承诺在异步工作结束后给出明确的信号!
Promise
语法:
let promise = new Promise(function(resolve,reject){ // 异步工作})
通过以上语法,我们就可以把异步工作封装成一个Promise
。在创建Promise
时传入的函数就是处理异步工作的方法,又被称为executor
(执行者)。
resolve
和reject
是由JavaScript
自身提供的回调函数,当executor
执行完了任务就可以调用:
resolve(result)
——如果成功完成,并返回结果result
;reject(error)
——如果执行是失败并产生error
;executor
会在Promise
创建完成后立即自动执行,其执行状态会改变Promise
内部属性的状态:
state
——最初是pending
,然后在resolve
被调用后转为fulfilled
,或者在reject
被调用时变为rejected
;result
——最初时undefined
,然后在resolve(value)
被调用后变为value
,或者在reject
被调用后变为error
;文件模块的fs.readFile
就是一个异步函数,我们可以通过在executor
中执行文件读取操作,从而实现对异步工作的封装。
以下代码封装了fs.readFile
函数,并使用resolve(data)
处理成功结果,使用reject(err)
处理失败的结果。
代码如下:
let promise = new Promise((resolve, reject) => { fs.readFile('1.txt', (err, data) => { console.log('读取1.txt') if (err) reject(err) resolve(data) })})
如果我们执行这段代码,就会输出“读取1.txt”字样,证明在创建Promise
后立刻就执行了文件读取操作。
Promise
内部封装的通常都是异步代码,但是并不是只能封装异步代码。
以上Promise
案例封装了读取文件操作,当完成创建后就会立即读取文件。如果想要获取Promise
执行的结果,就需要使用then
、catch
和finally
三个方法。
Promise
的then
方法可以用来处理Promise
执行完成后的工作,它接收两个回调参数,语法如下:
promise.then(function(result),function(error))
result
就是resolve
接收的值;error
就是reject
接收的参数;举例:
let promise = new Promise((resolve, reject) => { fs.readFile('1.txt', (err, data) => { console.log('读取1.txt') if (err) reject(err) resolve(data) })})promise.then( (data) => { console.log('成功执行,结果是' + data.toString()) }, (err) => { console.log('执行失败,错误是' + err.message) })
如果文件读取成功执行,会调用第一个函数:
PS E:\Code\Node\demos\03-callback> node .\index.js 读取1.txt 成功执行,结果是1
删掉1.txt
,执行失败,就会调用第二个函数:
PS E:\Code\Node\demos\03-callback> node .\index.js 读取1.txt 执行失败,错误是ENOENT: no such file or directory, open 'E:\Code\Node\demos\03-callback\1.txt'
如果我们只关注成功执行的结果,可以只传入一个回调函数:
promise.then((data)=>{ console.log('成功执行,结果是' + data.toString())})
到这里我们就是实现了一次文件的异步读取操作。
如果我们只关注失败的结果,可以把第一个then
的回调传null
:promise.then(null,(err)=>{...})
。
亦或者采用更优雅的方式:promise.catch((err)=>{...})
let promise = new Promise((resolve, reject) => { fs.readFile('1.txt', (err, data) => { console.log('读取1.txt') if (err) reject(err) resolve(data) })})promise.catch((err)=>{ console.log(err.message)})
.catch((err)=>{...})
和then(null,(err)=>{...})
作用完全相同。
.finally
是promise
不论结果如何都会执行的函数,和try...catch...
语法中的finally
用途一样,都可以处理和结果无关的操作。
例如:
new Promise((resolve,reject)=>{ //something...}).finally(()=>{console.log('不论结果都要执行')}).then(result=>{...}, err=>{...})
finally
回调没有参数,不论成功与否都会执行finally
会传递promise
的结果,所以在finally
后仍然可以.then
现在,我们有一个需求:使用fs.readFile()
方法顺序读取10个文件,并把十个文件的内容顺序输出。
由于fs.readFile()
本身是异步的,我们必须使用回调嵌套的方式,代码如下:
fs.readFile('1.txt', (err, data) => { console.log(data.toString()) //1 fs.readFile('2.txt', (err, data) => { console.log(data.toString()) fs.readFile('3.txt', (err, data) => { console.log(data.toString()) fs.readFile('4.txt', (err, data) => { console.log(data.toString()) fs.readFile('5.txt', (err, data) => { console.log(data.toString()) fs.readFile('6.txt', (err, data) => { console.log(data.toString()) fs.readFile('7.txt', (err, data) => { console.log(data.toString()) fs.readFile('8.txt', (err, data) => { console.log(data.toString()) fs.readFile('9.txt', (err, data) => { console.log(data.toString()) fs.readFile('10.txt', (err, data) => { console.log(data.toString()) // ==> 地狱之门 }) }) }) }) }) }) }) }) })})
虽然以上代码能够完成任务,但是随着调用嵌套的增加,代码层次变得更深,维护难度也随之增加,尤其是我们使用的是可能包含了很多循环和条件语句的真实代码,而不是例子中简单的 console.log(...)
。
如果我们不使用回调,直接把fs.readFile()
顺序的按照如下代码调用一遍,会发生什么呢?
//注意:这是错误的写法fs.readFile('1.txt', (err, data) => { console.log(data.toString())})fs.readFile('2.txt', (err, data) => { console.log(data.toString())})fs.readFile('3.txt', (err, data) => { console.log(data.toString())})fs.readFile('4.txt', (err, data) => { console.log(data.toString())})fs.readFile('5.txt', (err, data) => { console.log(data.toString())})fs.readFile('6.txt', (err, data) => { console.log(data.toString())})fs.readFile('7.txt', (err, data) => { console.log(data.toString())})fs.readFile('8.txt', (err, data) => { console.log(data.toString())})fs.readFile('9.txt', (err, data) => { console.log(data.toString())})fs.readFile('10.txt', (err, data) => { console.log(data.toString())})
以下是我测试的结果(每次执行的结果都是不一样的):
PS E:\Code\Node\demos\03-callback> node .\index.js12346957108
产生这种非顺序结果的原因是异步,并非多线程并行,异步在单线程里就可以实现。
之所以在这里使用这个错误的案例,是为了强调异步的概念,如果不理解为什么会产生这种结果,一定要回头补课了!
使用Promise
解决异步顺序文件读取的思路:
promise1
,并使用resolve
返回结果promise1.then
接收并输出文件读取结果promise1.then
中创建一个新的promise2
对象,并返回promise2.then
接收并输出读取结果promise2.then
中创建一个新的promise3
对象,并返回promise3.then
接收并输出读取结果代码如下:
let promise1 = new Promise((resolve, reject) => { fs.readFile('1.txt', (err, data) => { if (err) reject(err) resolve(data) })})let promise2 = promise1.then( data => { console.log(data.toString()) return new Promise((resolve, reject) => { fs.readFile('2.txt', (err, data) => { if (err) reject(err) resolve(data) }) }) })let promise3 = promise2.then( data => { console.log(data.toString()) return new Promise((resolve, reject) => { fs.readFile('3.txt', (err, data) => { if (err) reject(err) resolve(data) }) }) })let promise4 = promise3.then( data => { console.log(data.toString()) //..... })... ...
这样我们就把原本嵌套的回调地狱写成了线性模式。
但是代码还存在一个问题,虽然代码从管理上变的美丽了,但是大大增加了代码的长度。
以上代码过于冗长,我们可以通过两个步骤,降低代码量:
promise
的变量创建,将.then
链接起来代码如下:
function myReadFile(path) { return new Promise((resolve, reject) => { fs.readFile(path, (err, data) => { if (err) reject(err) console.log(data.toString()) resolve() }) })}myReadFile('1.txt') .then(data => { return myReadFile('2.txt') }) .then(data => { return myReadFile('3.txt') }) .then(data => { return myReadFile('4.txt') }) .then(data => { return myReadFile('5.txt') }) .then(data => { return myReadFile('6.txt') }) .then(data => { return myReadFile('7.txt') }) .then(data => { return myReadFile('8.txt') }) .then(data => { return myReadFile('9.txt') }) .then(data => { return myReadFile('10.txt') })
由于myReadFile
方法会返回一个新的Promise
,我们可以直接执行.then
方法,这种编程方式被称为链式编程。
代码执行结果如下:
PS E:\Code\Node\demos\03-callback> node .\index.js12345678910
这样就完成了异步且顺序的文件读取操作。
注意:在每一步的
.then
方法中都必须返回一个新的Promise
对象,否则接收到的将是上一个旧的Promise
。这是因为每个
then
方法都会把它的Promise
继续向下传递。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
目录ant design vue的form表单取值官方中有以下两种取值方式ant design of vue 学习之表单formv-decorator(表单验证,内置绑定,初始值)数据获取与填充表单实例ant design vue的form表单取值官方中有以下两种取值方式因为不是很熟悉,所以还是查了文档找了一下使用方式
这篇文章主要介绍的是 vue定义私有过滤器和基本使用,下面文章围绕vue定义私有过滤器的相关资料展开内容,需要的朋友可以参考一下,希望对大家有所帮助
本篇文章给大家带来了关于JavaScript的相关知识,其中主要整理了函数的定义与基本使用的相关问题,包括了用函数语句定义、函数的调用、未定义的实参等等内容,下面一起来看一下,希望对大家有帮助。
这篇文章给大家分享的是jQuery改变td背景色的方法,在jQuery中,可以利用css()方法来改变td单元格的背景颜色,文中的示例介绍得很详细,有需要的朋友可以参考,接下来就跟随小编一起了解看看吧。
说明: el-table有一个表格一级数据和二级数据显示的是一样的,像这种就可以用递归实现。数据结构是这样子的:tableData:[{name: Lucy,age: 18,mobile: 11111111111,type: 1,friends:[{name: Lucy-friend1,age: 16,mobile:
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008