前言:
1.Promise原理解析
2.后面部分讲 then,resolve与reject,all,race的操作
一.Promise原理
先看原理图:
①执行new Promise时会返回一个空状态(pending挂起状态),此时返回的是undefined(既不是成功,也不是失败
②Promise内部有两个方法,resolve和reject。resolve是将pending状态变为完成(fulfilled)状态,返回结果(result);而reject是将pending状态变为拒绝(rejected)状态,返回结果error。切记,状态是不可逆的,只能从pending到fulfilled或rejected。
二.Promise.prototype.then(onFulfilled, onRejected)方法
1.只要是promise对象就支持Promise语法,就能用then。
2.then支持两个参数,两个参数onFulfilled,onFulfilled都是函数类型的,而且onFulfilled和onFulfilled对应Promise的resolve和reject两个方法,因为这两个方法返回两个不同的状态.
3.如果then里面传的是非函数,就会返回一个空的Promise对象,这家就能保证调用then就一定能够返回一个then对象,这样子就能完全保证我们能够实用连续的then的链式调用.
代码1:
function loadScript (src) {
return new Promise((resolve, reject) => {
let script = document.createElement("script");
script.src = src;
script.onload = () => resolve(src);
script.onerror = (err) => reject(err);
document.head.append(script);
})
}
loadScript("./1.js")
.then(loadScript("./2.js"))
.then(loadScript("./3.js"))
//返回1 2 3
//./1.js ./2.js ./3.js均来自静态文件
说明:①上面代码中,then里面提供非函数 (nonfunction) 参数,那么 then
方法将会丢失关于该状态的回调函数信息,但是并不会产生错误,上面代码返回的pending状态。
代码2:
function loadScript (src) {
return new Promise((resolve, reject) => {
let script = document.createElement("script");
script.src = src;
script.onload = () => resolve(src);
script.onerror = (err) => reject(err);
document.head.append(script);
})
}
loadScript("./1.js")
.then(() => {
// 如果不加return则返回一个空的Promise对象,因为loadScript('./4.js')不是函数
// 如果加上return就是返回一个新的Promise实例去影响下一个then的状态
return loadScript('./42.js')
}, (err) => {
console.log(err)
})
.then(() => {
loadScript('./3.js')
}, (err) => {
console.log(err)
})
说明:
①如果不加return,依旧是个非函数(因为loadScript('./4.js')不是函数),则返回一个空的Promise对象。
②如果加上return就是返回一个新的Promise实例去影响下一个then的状态
代码3:then的正规操作
function loadScript (src) {
return new Promise((resolve, reject) => {
let script = document.createElement("script");
script.src = src;
script.onload = () => resolve(src);
script.onerror = (err) => reject(err);
document.head.append(script);
})
}
loadScript("./4.js").then((value) => {
console.log(value)
}, (err) => {
console.log(err)
})
结果:
说明:这是正常执行的结果,里面写的都是函数
三、Promise.resolve与Promise.reject的使用
一般情况下我们都会使用 new Promise() 来创建 Promise 对象,但是除此之外我们也可以使用其他方法。在这里,我们将会学习如何使用 Promise.resolve 和 Promise.reject 这两个静态方法。
代码:
function cap (type) {
if(type){
return Promise.resolve(42)
} else {
return Promise.reject(new Error("出错了"))
}
}
cap(0).then((value) => {
console.log(value)
}, (err) => {
console.log(err)
})
结果:
说明:
①如果直接返回的是数据,而且又想使用then方法,可以直接用静态方法里面写入数据,然后用then去调用,如上代码所示。
四、Promise.prototype.catch()方法
捕获异常是程序质量保障最基本的要求,可以使用 Promise 对象的 catch 方法来捕获异步操作过程中出现的任何异常。
代码1:
function loadScript (src) {
return new Promise((resolve, reject) => {
let script = document.createElement("script");
script.src = src;
script.onload = () => resolve(src);
script.onerror = (err) => reject(err);
document.head.append(script);
})
}
loadScript("./1.js")
.then(() => {
return loadScript('./42.js')
})
.then(() => {
loadScript('./3.js')
}).catch(err => {
console.log(err)
})
结果:
说明:catch() 方法返回一个Promise,并且处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。
代码2:代码比较
// 代码1
function test () {
return new Promise((resolve, reject) => {
throw new Error('wrong')
})
}
test().catch((e) => {
console.log(e.message) // wrong
})
// 代码2
function test () {
return new Promise((resolve, reject) => {
reject(new Error('es'))
})
}
test().catch((e) => {
console.log(e.message) // es
})
问题:这个代码展示了如何使用 catch 捕获 Promise 对象中的异常,有的同学会问 catch 捕获的是 Promise 内部的 Error 还是 Reject?上面的示例既用了 reject 也用了 Error,到底是哪个触发的这个捕获呢?
答:如上代码,代码1对比着上个代码2就能明显感受出来的,throw Error 和 reject 都触发了 catch 的捕获,而第2个用法中虽然也有 Error 但是它不是 throw,只是 reject 的参数是 Error 对象,换句话说 new Error 不会触发 catch,而是 reject。
说明:不建议在 Promise 内部使用 throw 来触发异常,而是使用 reject(new Error())
的方式来做,因为 throw 的方式并没有改变 Pronise 的状态
五、Promise.all(promiseArray)方法
var p1 = Promise.resolve(1)
var p2 = Promise.resolve(2)
var p3 = Promise.resolve(3)
Promise.all([p1, p2, p3]).then(function (results) {
console.log(results) // [1, 2, 3]
})
说明:
①Promise.all 生成并返回一个新的 Promise 对象,所以它可以使用 Promise 实例的所有方法。参数传递promise数组中所有的 Promise 对象都变为resolve的时候,该方法才会返回, 新创建的 Promise 则会使用这些 promise 的值。
②如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的 Promise 对象。
③由于参数数组中的每个元素都是由 Promise.resolve 包装(wrap)的,所以Paomise.all 可以处理不同类型的 promose对象。
六、 Promise.race(promiseArray)方法
场景:比如说有两条线路,你想快速的获得某一条线路等等,优点像CDN
返回值:一个待定的 Promise 只要给定的迭代中的一个promise解决或拒绝,就采用第一个promise的值作为它的值,从而异步地解析或拒绝(一旦堆栈为空)。
代码:
const fn1 = () => {
return new Promise((resolve, reject) =>{
setTimeout(()=>{
resolve(111)
},2000)
})
}
const fn2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() =>{
resolve(222)
}, 1000)
})
}
Promise.race([fn1(), fn2()]).then((value) => {
console.log(value)//222
})
说明:
①因为fn2的时间在1000mm,而fn1的时间在2000mm,所以fn2快于fn1,所以fn2先返回了
七、用Promise写一个接口
function getUrl (url) {
return new Promise((resolve, reject) => {
let xml = new XMLHttpRequest()
xml.onreadystatechange = function() {
if (xml.ready === 4) {
if (xml.status === 200) {
resolve(JSON.parse(xml.responseText))
}
else if (xml.status === 404) {
reject(new Error('错误信息404'))
}
}
}
xml.send(null)
})
}
const url = './jsonp.json'
getUrl(url).then((res) => {
console.log(res)
})