Promise原理和使用
Promise原理和使用
- Promise基本用法
- 嵌套的Promise
- 异步加载图片实例
- Ajax异步操作实例
- Promise新建后立即执行
- promise实例作为参数
- 调用resolve或reject不会终结 Promise的参数函数的执行
- 模拟一个Promise的最简单实现
- Promise接口超时模拟
Promise基本用法
Promise
是一个构造函数,接受一个参数:callback
,我们把要执行的异步任务放置在这个callback
中,当这个Promise
被实例化的时候,会立即执行这个callback
。创建的Promise
实例默认状态是pending
,通过传入的resolve
和reject
函数,可以将状态改成resolved
和rejected
(then
和catch
就是在这个状态发生改变的时候触发的)。
Promise
对象用来生成Promise
实例,传入一个函数(callback
)作为参数,该函数的两个参数分别是resolve
和reject
,它们是两个函数,由JavaScript引擎提供,不用自己部署。
其中,resolve
函数在异步操作成功时调用,并将异步操作的结果作为参数传递出去;
reject
函数在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
来看一个Promise
的例子:
/* Promise基本用法 */
function timeout(ms) {return new Promise((resolve, reject) => {setTimeout(resolve, ms, 'done')})
}
timeout(100).then((value) => {console.log(value)
})
Promise
新建后就会立即执行。
上面代码中,timeout
方法返回一个Promise
实例,表示一段时间以后才会发生的结果,这里的setTimeout
相当于一个异步操作,过了100ms
以后异步操作成功返回后调用resolve
,此时done
作为resolve
的参数同样会传给then
的第一个函数。
嵌套的Promise
let p1 = new Promise((resolve) => {setTimeout(function() {console.log(1)resolve()}, 1000)
})p1.then(() => {console.log(2)
}).then(() => {console.log(3)
}).then(() => {console.log(4)
})
// 1
// 2
// 3
// 4
then
方法(和catch
方法)执行后的内部都会返回一个新的Promise
对象,且默认状态都是resolved
,所以才能一直then
下去,如果我们将p1
的状态改变一下试试
let p1 = new Promise((resolve, reject) => {setTimeout(function() {console.log(1)reject()}, 1000)
})
p1.then(() => {console.log(2)
}, () => {console.log('a')
}).then(() => {console.log(3)
}).then(() => {console.log(4)
})
// 1
// a
// 3
// 4
如果我们需要执行的then
返回的是失败的状态,需要手动返回一个失败的Promise
对象,如果我们希望捕获这些错误怎么做呢?
let p1 = new Promise((resolve, reject) => {setTimeout(function() {console.log(1)reject()}, 1000)
})
p1.then(() => {console.log(2)
}, () => {console.log('a')
}).then(() => {console.log(3)return Promise.reject()
}).then(() => {console.log(4)
}).catch(() => {console.log('错了')
})
// 1
// a
// 3
// 错了
这样最后一个then
就不会触发,因为上一个状态是rejected
异步加载图片实例
再来一个异步加载图片的例子比较接地气:
function loadImageAsync(url) {return new Promise(function(resolve, reject) {const image = new Image()image.src = urlimage.onload = function() {resolve(image)}image.onerror = function() {reject(new Error('Could not load image at ' + url));}})
}
这里例子就很直观了,使用Promise
包装了一个图片加载的异步操作,如果加载成功,就调用resolve
方法,否则就调用reject
方法。
Ajax异步操作实例
下面是用Promise
对象实现Ajax
操作的例子:
const getJSON = function(url) {const promise = new Promise(function(resolve, reject){const handler = function() {if (this.readyState !== 4) {return}if (this.status === 200) {resolve(this.response)} else {reject(new Error(this.statusText))}}const client = new XMLHttpRequest()client.open("GET", url)client.onreadystatechange = handlerclient.responseType = "json"client.setRequestHeader("Accept", "application/json")client.send()})return promise
}getJSON("/posts.json").then(function(json) {console.log('Contents: ' + json)
}, function(error) {console.error('出错了', error)
})
上面代码中,getJSON
是对XMLHttpRequest
对象的封装,用于发出一个针对JSON
数据的HTTP
请求,并且返回一个Promise
对象。如果调用resolve
函数和reject
函数时带有参数,那么它们的参数会被传递给回调函数
Promise新建后立即执行
先上例子:
/* Promise新建后会立即执行 */let promise = new Promise((resolve) => {console.log("Promise")resolve()console.log("!!!")
})promise.then(function() {console.log("resolved.");
})
console.log("Hi!")// Promise
// !!!
// Hi!
// resolved
上面代码中,总结就是以下两点:
Promise
实例新建后会立即执行- then方法指定的回调函数会在当前脚本所有同步任务执行完才会执行(所以这里的
resolve
最后才会输出)
promise实例作为参数
resolve
函数的参数除了正常的值以外,还可能是另一个Promise
实例:
const p1 = new Promise(function (resolve, reject) {// ...
})const p2 = new Promise(function (resolve, reject) {// ...resolve(p1)
})
即,一个异步操作的结果是返回另一个异步操作。
注意,这时p1
的状态就会传递给p2
,也就是说,p1
的状态决定了p2
的状态。如果p1
的状态是pending
,那么p2
的回调函数就会等待p1
的状态改变;如果p1
的状态已经是resolved
或者rejected
,那么p2
的回调函数将会立刻执行。
const p1 = new Promise(function (resolve, reject) {console.log('进入p1函数')setTimeout(() => {console.log('进入p1的setTimeout')return reject(new Error('fail'))}, 3000)
})const p2 = new Promise(function (resolve, reject) {console.log('进入p2函数')setTimeout(() => {console.log('进入p2的setTimeout')return resolve(p1)}, 1000)
})p2.then(result => console.log(result)).catch(error => console.log(error))//进入p1函数
//进入p2函数
//进入p2的setTimeout
//进入p1的setTimeout
//Error: fail
前面也说过,Promise
新建后就会立即执行,所以直接就会进入函数体。由于p2
返回的是另一个Promise
,导致p2
自己的状态无效了,由p1
的状态决定p2
的状态。所以,后面的then
语句都变成针对后者(p1
)。又过了 2 秒,p1
变为rejected
,导致触发catch
方法指定的回调函数。(这里需要知道JavaScript的单线程异步执行机制)
调用resolve或reject不会终结 Promise的参数函数的执行
具体什么意思呢,看下面的例子:
new Promise((resolve, reject) => {resolve(1)console.log(2)
}).then(r => {console.log(r)
})
// 2
// 1
上面代码中,调用resolve(1)
以后,后面的console.log(2)
还是会执行,并且会首先打印出来。这是因为立即 resolved
的Promise
是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
一般来说,调用resolve
或reject
以后,Promise
的使命就完成了,后继操作应该放到then
方法里面,而不应该直接写在resolve
或reject
的后面。所以,最好在它们前面加上return
语句,这样就不会有意外。
new Promise((resolve, reject) => {return resolve(1)// 后面的语句不会执行console.log(2)
})
模拟一个Promise的最简单实现
/* 模拟一个Promise的最简单实现 */
function Promise(executor){ //executor执行器let self = this;self.status = 'pending'; //等待态self.value = undefined; // 表示当前成功的值self.reason = undefined; // 表示是失败的值function resolve(value){ // 成功的方法if(self.status === 'pending'){self.status = 'resolved';self.value = value;}}function reject(reason){ //失败的方法if(self.status === 'pending'){self.status = 'rejected';self.reason = reason;}}executor(resolve,reject);
}Promise.prototype.then = function(onFufiled,onRejected){let self = this;if(self.status === 'resolved'){onFufiled(self.value);}if(self.status === 'rejected'){onRejected(self.reason);}
}
module.exports = Promise;
拿开始的例子来说
function timeout(ms) {return new Promise((resolve, reject) => {setTimeout(resolve, ms, 'done')})
}
timeout(100).then((value) => {console.log(value)
})
100ms
后调用resolve
函数,resolve
函数内部将状态设为了resolved
了,然后在本脚本其他同步惹任务都完成后执行then
方法返回的回调函数,此时将value
值传给then
方法的回调函数。
Promise接口超时模拟
如果一个接口访问时间超过400毫秒就算超时,用Promise
如何解决
传统的解决思路设个定时器,设个全局变量data接受接口要返回的数据,如果400毫秒后定时器的全局data还未被赋值,说明接口请求超时,当然这种做法不够优雅,可以使用Promise
来实现:
// 接口超时模拟1
function ajaxTimeOut(){return new Promise(function(resolve,reject){let data = null// ajax操作用setTimeout模拟setTimeout(function(){data = '真实数据来了'},388)setTimeout(function(){if(data === null){reject('接口访问超时')} else {resolve(data)}},400)})
}
ajaxTimeOut().then(function(data){console.log('success,data is' + data)
}).catch(function(err){console.log('err信息:'+err)
})
虽然看起来好像是解决了,但是怎么看怎么像愚蠢的解决方案嵌套了一层美丽的皮囊,下面结合Promise
的race
方法来更好的解决超时需求:
// 接口超时模拟2
function ajax(){return new Promise(function(resolve,reject){setTimeout(function(){resolve('success')},2000)})
}
function timeOut(){return new Promise(function(resolve,reject){setTimeout(function(){reject('超时')},400)})
}
Promise.race([ajax(),timeOut()])
.then(function(data){console.log(data)
})
.catch(function(err){console.log(err)
})
Promise原理和使用相关推荐
- 面试题promise原理
面试题Promise原理 在Promise的内部,有一个状态管理器的存在,有三种状态:pending.fulfilled.rejected. (1) promise 对象初始化状态为 pending. ...
- 从零开始,手写完整的Promise原理!
珠峰十年深度沉淀,最具诚意与深度的课程限时免费开放,带你从0到1完美诠释异步编程,并手写一个完整的promise原理! [扫码免费参加,限200人] (名额有限,扫描上方二维码立即参加!) 历史学员 ...
- js中promise原理及手动基本实现_V1
前言 这几天面试过程,有个面试官突然跟我抠上了promise的实现原理,虽然有所准备,但是没能清晰地说出其中的原理,所以有点遗憾!!!,但是事已至此,只能默默去查了相关资料深入其中了解一番.因此就有了 ...
- Promise原理详解及实现方式
在异步编程中,许多操作都会放在回调函数(callback)中,有时候需要拿到上一个异步操作的返回值再做第二次请求 比如: asyncOperation(data => {// 处理 `data` ...
- ES6 Promise原理
ES6 Promise原理 一.Promise是什么 二.为什么会有Promise 1.回调地狱 + 异步同步事件调用顺序带来的双重伤害 2.回调事件的分离 三.Promise的三种状态 1.reso ...
- promise原理与async 及 await
promise原理与async 及 await 1.1 Promise是一个构造函数 1.2 Promise优缺点 1.3 async 及 await 1.4 相较于 Promise,async/aw ...
- 简述promise原理
一. promise应用场景 1 解决回调地狱 比如我们经常可能需要异步请求一个数据之后作为下一个异步操作的入参 getData(function(a){ getMoreData(a, functio ...
- Promise原理及手写Promise
原理: Promise 原理围绕以下三个问题进行解决: (有任何一步不了解的话请先往下看手写Promise,会一步步剖析原理,看完后再继续回顾这里!!) 1. 怎么实现异步? Promise内部the ...
- Promise原理分析
前言 最近讨论到了 Promise,此前知道也使用过它,但是对于其原理却不甚了解. 于是翻了翻 MDN 上的文档,又找了几篇文章看了看,研究了研究. 最终,自己尝试了一番,对于其原理也有所了解. Pr ...
最新文章
- mysql The server quit without updating PID file异常解决办法
- Excel为整列设置函数
- 使用JMeter和Yourkit进行REST / HTTP服务的性能分析
- Displaying a Refresh Control for Table Views
- php和c语言的字符数组中,字符数组和字符串的区别,C语言字符数组和字符串区别详解...
- swift python javascript_最小的Swift App
- java程序设计自考_java程序设计自考试题
- 想招到优秀的程序员?这些坑一定不要踩
- 计算同比和环比的区别_【数据说第三期】同比和环比数据分析时,有哪些需要注意的点?...
- distcp用于集群中数据传输解读
- 24. Swap Nodes in Pairs 1
- win10怎么卸载Edge浏览器
- html制作作业提交入口,最全的提交网站入口大全
- CAShapeLayer把图片做成圆形效果
- 电子计算机管理人事资料的准则,{转帖}关于中国的一些人事制度,职称评定
- JavaScript:自动生成博文目录导航
- 微信小程序云开发数据库操作
- html5如何获取音频资源6,【已解决】如何从喜马拉雅的页面中获取到mp3音频文件...
- Java学习笔记之 Lambda表达式
- node.js学习总结:node.js的内置模块,模块化,npm与包 express,前后端身份认证 JWT认证机制