ES6 Promise
陈丶陈奎宁promise /ˈprɑːmɪs/ 承诺;许诺;保证
Promise的含义
Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件更合理、更强大。ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。
我们来模拟传统的解决方案
const url = 'https://jsonplaceholder.typicode.com/todos'
// 它是由浏览器提供的,不是JavaScript原生的
const xhr = new XMLHttpRequest()
let btnDom = document.getElementById('btn')
const handler = function() {
}
// 绑定点击事件
btnDom.onclick = function () {
xhr.onreadysrtatechange = fucntion() {
// readyState表示请求/响应过程中当前的活动阶段0 1 2 3 4
if (xhr.readyState === 4) {
if (xhr.status >=200 && xhr.status <= 300) {
const data = xhr.responseText;
// 拿到数据后如果还需要在做发送请求的动作
// 我们就需要在onreadysrtatechange的回调中继续写代码
// 这样代码就会嵌套很多层 也被称为回调地狱
} else {
console.log('Response was unsuccessful ' + xhr.status)
}
}
}
xhr.open('get', 'http://www.baidu.com')
xhr.setRequesetHeader('customHeader', 'customHeaderValue')
// send的参数不能为空,get请求传递null
xhr.send(null)
}
所谓Promise,简单来说就是一个容器,里面保存着某个未来才会结束的事件的结果(通常是一个异步操作)
它有两个特点:
- 对象的状态不受外界的影响。它有3个状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)只有异步操作的结果「 (resolve, reject) => { 异步操作返回结果后调用 resolve(value) 或者 reject(err) } 」可以决定当前是哪种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来。
- 一旦状态发生改变,就不会再变。任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从 pending 到 fulfilled 和 从 pending 到 rejected
它也有一些缺点:
- 无法取消Promise,一旦新建它会立即执行,无法中途取消。
- 如果不设置回调函数「.then() 和 .catch() 」Promise内部抛出的错误,不会反应到外部。
ES6规定,Promise是一个构造函数,用来生成Promise实例
const promise = new Promise(function(resolve, rejecte) {
// some code
if (/*异步操作成功*/) {
resolve(value)
} else {
reject(error)
}
})
Promise构造函数 接受一个函数作为参数,该函数 的 两个参数 分别是 resolve 和 reject ,它们又是两个函数,由JavaScript引擎提供,不用自己部署。
resolve函数的作用是,将 Promise 对象的状态 从 pending 变为 resolved 。在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。
reject函数的作用是,将 Promise 对象的状态 从 pending 变为 rejecetd 。在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用 then 方法指定 resolved 状态 和 rejected 状态 的回调函数
promise.then(res => {
// success
}, err => {
// error
)
then方法接受两个回调函数作为参数。
第一个回调函数是Promise对象的状态变为resolved时调用
第二个回调函数是Promise对象的状态变为rejected时调用
这两个函数都是可选的,不一定要提供。它们都接受Promise对象传出的值( res 和 err )作为参数。
function timeout(ms) {
// 返回Promise实例对象 用来.then后续的操作
return new Promise((resolve, rejecte) = > {
setTimeout(() => {
resolve('success setTimeout ' + ms)
}, ms)
// 简写
setTimeout(resolve, ms)
})
}
// 过了ms后,Promise实例的状态变为resolved
// 就会触发.then方法绑定的回调函数
timeout(3000).then(res => {
console.log(res) // success setTimeout 3000
})
Promise 新建后就会立即执行
let promise = new Promse((resolve, reject) => {
console.log('Promise')
resolve()
})
promise.then(() => {
console.log('resolved')
})
console.log('hello')
// 'Promise'
// 'hello'
// 'resolved'
异步加载图片的例子
function loadImage(url) {
return new Promise((resolve, reject) => {
const image = new Image()
image.onload = function() {
resolve(image)
}
image.onerror = function() {
reject(new Error('Could not load image at ' + url))
}
image.src = url
})
}
// 调用异步加载图片的方法
loadImage('https://p7.itc.cn/images01/20201104/87d0aa991d454770813dcb01260c2ed0.jpeg')
.then(res => {
// res <img src='https://p7.itc.cn/images01/20201104/87d0aa991d454770813dcb01260c2ed0.jpeg'>
console.log('res: ', res)
// 可以插入到页面某个位置
}).catch(error => {
console.log('error: ', error)
})
用Promise实现Ajax操作的例子
const getJSON = function(url) {
return new Promise((resolve, rejected) => {
const jsx = new XMLHttpRequest()
const handler = function() {
if (this.readyState !== 4) return
if (this.status >= 200 && this.status <= 300) {
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
jsx.open('get', url)
jsx.onreadystatechange = handler;
jsx.responseType = 'json'
jsx.send(null)
})
}
getJSON('/post.json').then(json => {
console.log(json)
}, error => {
console.log(error)
})
Promise.prototype.then()
Promise实例具有then方法,它的then方法是定义在原型对象 Promise.prototype 上的
then方法如果返回一个新的Promise实例(不是原来那个实例)因此可以采用链式写法,即then方法后面可以再调用另一个then方法
getJSON('/post.json').then(json => {
return getJSON('/post.json?id=' + json.id)
// return getJSON(json.conmmentUrl)
}, error => {
console.log(error)
}).then(content => {
console.log(content)
})
Promise.prototype.catch()
一般来说,不要在then方法里面定义 rejected 状态的回调函数,总是使用catch方法
因为catch这种写法不仅可以捕获到 rejected 状态的错误,还可以捕获前面then方法执行中的错误,并且也更接近嗯同步的写法「try / catch」
跟传统的 try / catch 代码块不同的是,如果没有使用 catch 方法指定错误处理的回调函数, Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。通俗的说法就是'Promise 会吃掉错误'
const someThing = function() {
return new Promise((resolve, reject) => {
// 这行会报错,因为x没有声明
resolve(x + 2)
})
}
someThing().then(res => {
console.log('everything is great!')
})
setTimeout(() => console.log(123), 2000)
// ReferenceError: x is not defined
// 123
上述代码控制台会报出错误,但是不会退出进程、终止脚本执行,2s后还是会输出123
这就是说 Promise 内部的错误不会影响到 Promise 外部的代码,造成 'Promise会吃掉错误'
显然这样的行为是不可取的
所以,也总是建议 Promise 对象后面要跟 catch 方法,这样可以处理 Promise 内部发生的错误。
Promise.prototype.finally()
finally方法用于不管 Promise 对象最后的状态如何,都会执行的操作。
promise
.then(result => { ... })
.catch(err => { ... })
.finally(() => { ... })
上面代码中,不管 promise 最后的状态,在执行完 then 方法 或 catch 方法的回调函数以后,都会执行finally方法指定的回调函数。
finally方法的回调函数不接受任何参数,这意味着没有办法知道前面的 Promise 状态到底是 resolved 还是 rejected ,这表明,finally方法里的操作,应该是与状态无关的,不依赖于Promise执行的结果。
Promise.all()
all方法将拥有多个 Promise 实例,包装成一个新的 Promise 实例
// p1 p2 p3 都是Promise实例 如果不是Promise实例
// 将会调用Promise.resolve()方法转为Promise实例
const p = Promise.all([p1, p2, p3])
p的状态有两种情况
- 只有 p1 p2 p3 的状态都变成 fulfilled ,p的状态才会变成 fulfilled 此时,p1 p2 p3的返回值组成一个数组,传递给p的回调函数
- 只要 p1 p2 p3 之中有一个状态被 rejected ,p的状态就会变成 rejected ,此时第一个被 rejected 的实例的返回值,会传递给p的回调函数
const promises = [2, 3, 5, 7, 11, 13].map((id) => {
return getJSON('post/' + id + '.json')
})
Promises.all(promises)
.then(posts => { ... })
.catch(reason => { ... })
catch方法返回的是一个新的 Promise 实例(该实例执行完catch方法后,也会变成resolved),catch后面可以继续链式调用then方法,结合all方法看下
const p1 = new Promise((resolve, reject) => {
resolve('hello')
})
.then(result => result)
.catch(e => e)
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了')
})
.then(result => result)
.catch(e => e)
Promise.all([p1, p2])
.then(result => console.log(result))
// 不会触发catch方法
.catch(e => console.log(e))
// ['hello', Error:报错了]
因为catch方法返回的是一个新的Promise实例且resolved状态的,所以不会出发all方法的catch方法
Promise.race() 竞赛
race方法同样是将多个Promise实例,包装成一个新的Promise实例
const p = Promise.race([p1, p2, p3])
上面代码中,只要 p1 p2 p3 之中有一个实例率先改变状态,p的状态就跟着改变,那个率先改变的Promise实例的返回值,就传递给p的回调函数
下面的例子,如果一个请求指定时间内没有获取到结果,就抛出超时错误
const p = Promise.race([
fetch(url),
setTimeout(() => {
reject(new Error('time out'))
}, 5000)
])
p.then(data => {
console.log(data) // 拿到请求数据data
})
.catch(err => {
console.log(err) // time out
})
发布于 2024-01-04 22:19・IP 属地上海 标签:ES6,resolve,console,log,实例,Promise,catch From: https://www.cnblogs.com/sexintercourse/p/18119526