promise 使用的原因?
JavaScript是一门单线程语言,解决异步场景大部分使用回调函数来进行,
var asuncFunc = function(cb) {
setTimeout(function() {
cb()
}, 1000)
}
//多个异步函数嵌套
setTimeout(function() {
console。log("1")
setTimeout(function() {
console.log("2")
}, 1000)
}, 1000)
多个回调嵌套,很容易让代码变宽,为了更优雅的调用回调函数,ES6产生了一个新规范Promise, 他让异步操作的变得近乎同步化。
Promise基础
在支持ES6的高级浏览器环境中,我们通过 new primise() 即可创造一个promise实例
这个构造函数接受一个函数,分别接受两个参数,resolve和reject,代表着我们需要改变当前实例的状态到已完成 或者是已拒绝。
const promise1 = function() {
return new Promise(function(resolve, reject) {
//异步内容
setTimeout(function() {
//输出完成后,调用函数传入的resolve函数,将该promise实例标记为已完成,当前promise串行继续
resolve()
}, 1000)
})
}
const promise2 = function() {
return new Promise(function(resolve, reject){
setTimeout(function() {
resolve();
}, 2000)
})
}
串行两个promise实例,
promise1().then(function() { return promise2()})
或者是
promise1().then(function() {promise2})
promise1在执行后,状态从pending变成了fulfilled,执行了resolve,那么就会去执行then的方法,然后执行promise2()
如果我promise从pending变成了拒绝状态,执行reject方法,会进入到catch中
const promise3 = function() {
return new Promise(function(resolve, reject) {
var random = Math.random() * 10;
setTimeout(function() {
if(random >= 5) {
resolve(random)
}else {
reject(random)
}
}, 1000)
})
}
const onResolve = function(val) {
console.log('已完成' + val)
}
const onReject = function(val) {
console.log('已拒绝' + val)
}
//1.promise的then可以接受两个函数,第一个参数在resolve后执行,第二个参数在reject后执行
promise3.then(onResolve, onReject);
//2.catch拦截已拒绝状态
promise3.catch(onResolve).then(onReject)
//3.try catch
try {
promise3().then(onResolve)
}catch {
onReject()
}
总结
- promise有三个状态,pending 、fulfilled 、rejected,进行中的状态可以变更为fulfilled和rejected,已经变更过的状态无法继续变更。
- ES6中的Promise构造函数,构造之后传入一个函数,接受两个函数参数,执行第一个参数之后就会改变当前promise为fulfilled,执行第二个参数之后就会变为rejected状态。 3.通过.then方法,即可在上一个promise达到已完成时继续执行下一个函数或者promise。同时通过resolve或者reject时传入参数,即可给下一个函数或者promise传入初始值。 4.已拒绝的promise,后续可以通过.catch方法或是.then方法的第二个参数或是try catch进行捕获
封装异步操作为promise
//传统异步回调
function asyncFunc(cb) {
setTimeout(function() {
cb()
}, 1000)
}
var callback = function() {
console.log("2")
}
asyncFunc(callback)
//promise封装
const dynamicFuncAsync = function () {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve()
}, 1000)
})
}
const onResolve = function() {
console.log("2")
}
dynamicFuncAsync().then(onResolve)
ajax请求封装
function ajax(url, success, fail) {
var client = new XMLHttpRequest()
client.open('GET', url)
client.onreadystateChange = function() {
if(this.readyState !== 4) {
return;
}
if(this.status === 200) {
success(this.response)
}else {
fail(new Error(this.statusText))
}
}
client.send();
}
ajax('/ajax.json', function(){console.log('成功')」, function() { console.log('失败')})
const ajaxAsync = function(url) {
return new Promise(function(resolve, reject) {
var client = new XMLHttoRequest()
client.open('GET', url)
client.onreadystatechange = function() {
if(this.readyState !== 4) {
return;
}
if(this.status === 200) {
resolve(this.response)
}else {
reject(new Error(this.statusText))
}
}
client.send()
})
}
ajax('./ajax.json')
.catch(function() {
console.log('失败')
})
.then(function() {
console.log('成功')
})
*总结
- 可以把任何一个函数或者是异步函数改为promise,尤其是异步函数,改为promise之后即可进行链式调用,增强可读性。
- 将带有回调函数的异步改为promise,只需要在内部实例化promise之后,在原来执行回调函数的地方执行对应的更改promise状态的函数即可
promise规范解读
任何符合promise规范的对象或函数都可以成为promise。 promiseA plus 规范地址:
术语
Promise: promise是一个拥有then方法的对象或函数,其行为符合本规范。
具有then方法(thenable):是一个定义了then方法的对象或函数。
值(value):指任何JavaScript的合法指(包括 undefined,thenable 和promise)。
异常(exception):是使用throw语句抛出的一个值。
原因(reason):表示一个promise的拒绝原因。
要求
promise的状态
一个Promise的当前状态必须为以下三种状态中的一种: 等待态(Pending)、已完成(FulFilled)和拒绝(Rejected)。
- 处于等待态时,promise需满足以下条件:可以变为
已完成或已拒绝 - 处于已完成时,promise需满足以下条件: 1.不能迁移至其他任何状态, 2.必须拥有一个不可变的值
- 处于已拒绝时,promise需满足以下条件:1.不能迁移至其他任何状态,2.必须拥有一个不可变的原因
###必须有一个then方法
一个promise必须提供一个then方法以访问其当前值和原因。
promise的then方法接受两个参数:promise.then(onFullfiled, onRejected) 他们都是可选参数,同时他们都是函数,如果onFullfilled或onRejected不是函数,则需要忽略他们
-
如果
onFullfiled是一个函数- 当
promise执行结束后其必须被调用,其第一个参数为promise的结果 - 在
promise执行结束前,其不可被调用 - 其调用次数不可超过一次
- 当
-
如果
onRejected是一个函数- 当
promise被拒绝执行后其必须被调用,其第一个参数为promsie的结果 - 在
promise被拒绝执行前其不可被调用 - 其调用次数不可超过一次
- 当
-
在执行上下文堆栈仅包含平台代码之前,不可调用
onFulfiled或onRejected -
onFulfilled和onRejected必须被作为普通函数调用(即费实例化调用,这样函数内部this非严格模式下指向window) -
then方法可以被同一个promise调用多次- 当
promise成功执行时,所有onFulfilled需按照其注册顺序依次回调 - 当
pormise被拒绝执行时,所有的onRejected需按照其注册顺序依次回调
- 当
-
then方法必须返回一个promise对象promsie2 = pormise1.then(onFulfilled, onRejected);- 只要
onFulfilled或者onRejected返回一个值x,promise2都会进入onFulfilled状态
- 如果
onFulfilled或者onRejected返回一个异常e,则promise2必须拒绝执行,并返回拒因e
var promise1 = new Promise(function(resolve, reject) {reject(new Error('fail'))}) promise1 .catch( e => { console.log(e + '已拒绝') //Error: fail 已拒绝 })- 如果
onFulfilled不是函数且promise1状态变为已完成,promise2必须成功执行并返回相同的值
- 如果
onRejected不是函数且promise1状态变为已拒绝,promise2必须执行拒绝回调并返回相同拒因
var promise1 = new Promise(function(resolve, reject) {reject()}) promise1 .then(null, function() { return 123 }) .then(null, null) .then(null, null) .then( res1 => { console.log(res1 + '已完成') //res1已完成 }, e => { console.log(e + '已拒绝') } ) - 只要
promise的解决过程
Promise解决过程是一个抽象的操作,其需输入一个promise和一个值,我们表示为[[Resolve]](promise, x) (这句话的意思就是把promise resolve了,同时传入x作为值)
promise.then(function(x) {
console.log('会执行这个函数,同时传入x变量的值',x)
})
如果x有then方法且看上去像一个Promise,解决程序即尝试使promise接受x的状态;否则其用x的值来执行promise。
- 如果
promise和x指向同一对象,以TypeError为拒因拒绝执行promise - 如果x 为promsie
- 如果x处于等待态,promise需保持为等待态直至x被执行或拒绝
- 如果x处于执行态,用相同的值执行promise
- 如果x处于拒绝态,用相同的拒因拒绝promise
模拟部分promise源码
//合法的promise构造函数
class CustomPromise {
//构造函数
//handleFunc 每个函数都会有一个回调函数传进去,对应的回调函数有两个参数
constructor(handleFunc) {
//最小实现
//根据规范定义的东西
//规范1.3,1.4,1.5
this.state = 'pending'
this.value = undefined
this.fulfilledList = [];
//执行handleFunc,里面有两个参数
//绑定this,保证resolve方法能够传进去,this保有当前的上下文
handleFunc(this.triggerResolve.bind(this))
}
//就是传进去的resolve,reject方法,
triggerResolve(val) {
//为什么是异步,setTimeout,如果直接执行resolve函数,那么就会立即的执行.then ,打印出hello world,
//但是此时.then方法还没有注册
//在这时需要我们将所有的.then都收集起来,这样在执行triggerResolve的哪个.then
//2.2.4 在执行当前的上下文堆栈仅包含平台代码之前,不得调用onFulfilled 或者 onRejected
//平台代码:
//resolve调用在前,但是要将他放在队列之后
setTimeout(() => {
//已经进入了已完成的状态
if(this.state !== 'pending') return;
this.state = 'fulfilled'
this.value = val;
this.fulfilledList.forEach(item => item(val))
this.fulfilledList = []
}, 0)
}
//then方法收集依赖,回调函数
then(onFulfilled, onRejected) {
const { value, status } = this
//实现串联,返回一个新的promise
const promiseInstance = new CustomPromise((onNextFulfilled, onNextRejected) => {
//onFulfilled和onNextFulfilled联合起来
function onFinalFulfilled(val) {
if(typeof onFulfilled !== 'function') {
onNextFulfilled(val)
}else {
const res = onFulfilled(val)
if(typeof res.then === 'function') {
//res是一个promsie
res.then(onNextFulfilled)
}else {
onNextFulfilled(res);
}
}
}
switch(status) {
case 'pending': {
//第一次收集
this.fulfilledList.push(onFinalFulfilled);
break;
}
}
});
return promiseInstance;
}
catch(onRejected) {
return this.then(null, onRejected);
}
static all(list) {
return new CustomPromise((resolve, reject) => {
let count = 0;
const values = [];
for(const [i, CustomPromiseInstance] of list.entries()) {
CustomPromiseInstance
.then(
res => {
values[i] = res;
count++;
if(count === list.length) resolve(values);
},
err => {
reject(err);
}
)
}
})
}
}
//实例化一个promise
//根据特征判断,不能根据具体属性(instance)
// const promise = new CustomPromise(function(resolve) {
// resolve()
// })
//应用
const promise = function(time) {
return new Promise(function(resolve, reject) {
return setTimeout(resolve, time)
})
}
const promiseInstance = promise(1000)
promiseInstance.then(function() {
console.log('hello world')
})