共计 7670 个字符,预计需要花费 20 分钟才能阅读完成。
Promise
一种更优的异步编程对立 办法,如果间接应用传统的回调函数去实现简单操作就会造成回调深渊
// 回调深渊
$.get('/url1'() => {$.get('/url2'() => {$.get('/url3'() => {$.get('/url4'() => {$.get('/url5'() => {// 大略就是这样子的})
})
})
})
})
CommonJS
社区提出了 Promise
标准,在 ES2015
中被标准化,成为语言标准。当期待状态改编程胜利或者失败之后就再也不能再被扭转了,胜利的时候触发onFulfilled
回调,失败的时候触发onRejected
回调
Promise 简略应用
new Promise
传入一个回调函数,这个回调函数两个参数,第一个把 Promise
改成为胜利的状态,第二个参数把Promise
扭转成失败的状态,捕捉胜利和异样能够应用 .then
和.catch
办法,这两个办法返回的也是一个 Promise
对象
// 演示
const promsie = new Promise((resolve, reject) => {reject(1)
})
promsie.then((value) => {console.log(value)
}, (err) => {
// end 执行完之后才会执行这个
console.log(err)
})
// end 会先执行
console.log('end')
不论 Promise
中有没有异步操作,then 办法中的回调函数仍然会进入回调队列中排队,会等同步代码执行完之后才会执行
用 Promise
写一个申请函数
function ajax (url) {return new Promise((resove, reject) => {var xhr = new XMLHttpRequest()
xhr.open('GET', url)
// 新办法能够间接承受一个 j 对象
xhr.responseType = 'json'
xhr.onload = function () {if (this.status === 200) {resove(this.response)
} else {reject(new Error(this.statusText))
}
}
xhr.send()})
}
ajax('/json1.json').then(ret => {console.log(ret)
}).catch(err => {console.log(err)
})
如果须要多个间断的申请能够应用链式调用
ajax('/json1.json').then(ret => {return ajax('/json2.json')
}).then(ret => {return ajax('/json3.json')
}).then(ret => {return ajax('/json4.json')
})
这种链式调用是不是很相熟,在 jqeury
中也有链式调用,jquery
中是返回了自身这个对象所以能够实现链式调用,那么在 Promise
中是不是这样呢
let promsie1 = ajax('/json1.json')
let promise2 = promsie1.then(ret => {console.log(ret)
}).catch(err => {console.log(err)
})
console.log(promsie1 === promise2) // false
let a = $("body").attr('class', 'body')
let b = a.prop('disabled', true)
console.log(a === b) // true
通过测试发现,Promise
返回的是一个全新的 Promise
对象,返回全新的 Promise
对象的目标就是为了实现 Promise
的链条,每个 .then
办法负责不同的工作,互不烦扰,如果一直的链式调用 then
办法,这里的每个 then
办法都在为上一个 then
办法返回的 Promise
对象去增加状态明确后的回调,这些 Promise
会顺次执行,而且咱们能够在 then
办法中去手动返回一个 Promise
回调。如果 then
办法中的回调函数返回了值,则会给下一个 then
办法的回调函数传递这个返回的值,如果没有返回那么默认返回的就是 undefined
总结一下就是
Promise
对象的then
办法会返回一个全新的Promise
对象- 前面的
then
办法就是在为上一个then
返回的Promise
注册回调 - 后面的
then
办法中的回调函数的返回值回作为前面then
办法回调的参数 - 如果回调中返回的是
Promise
, 那前面的then
办法的回调会期待他的完结
捕捉异样
onRejected
回调会在 Promise
执行异样或者抛出的异样时触发,捕捉异样有两种形式,第一种, then(胜利解决的回调函数, 异样解决的回调函数)
在 then
办法中传递两个回调函数,第二种用 .catch
办法去捕捉异样,catch
办法其实就是 then
办法的别名,相当于 then
办法第一个参数传undefined
// then(胜利解决的回调函数, 异样解决的回调函数)
ajax('/json1.json').then(ret => {console.log(err)
}, err => {console.log(err)
})
// catch
ajax('/json1.json').then(ret => {console.log(err)
}).catch(err => {console.log(err)
})
// catch
ajax('/json1.json').then(ret => {console.log(err)
}).then(undefined,err => {console.log(err)
})
这两种形式还是有很大的差别,catch
其实是在给上一个 then
返回的 Promise
捕捉异样,然而如果是同一个链条下的Promise
的谬误会向下传递直到有 catch
办法捕捉,而 then
办法传递两个回调函数的捕捉异样的形式只会捕捉谁上一个 Promise
的谬误
ajax('/json1.json').then(ret => {console.log(ret)
}).then(undefined, err => {console.log(err)
}).then(ret => {console.log(ret)
}).then(ret => {console.log(ret)
})
// catch 捕捉异样
ajax('/json1.json').then(ret => {console.log(ret)
}).catch(err => {// 这里能捕捉之前的所有 Promise 的异样})
// 传递 then 第二个参数捕捉异样
ajax('/json1.json').then(ret => {console.log(ret)
}).then(undefined, err => {console.log(err)
throw new Error('成心的异样')
}, (err) => {// 这里能捕捉成心的谬误}).then(ret => {console.log(ret)
}).then(ret => {console.log(ret)
}).catch(err => {// 这个时候曾经捕捉不到异样了,因为上一个成心的异样曾经被捕捉了,依据 then 办法会返回一个 Promise 所以捕捉异样之后会返回一个胜利的 Promise})
还能够全局捕捉异样, 这种全局形式捕捉异样是不举荐应用的,应该在代码块中明确的去捕捉对应的异样
// 浏览器环境中
window.addEventListener('unhandledrejection', event => {console.log(event.reason, event.promise)
// reason 失败起因,// promise 失败的 Promise
event.preventDefault()}, false)
// nodejs 中
process.on('unhandledRejection', (reason, promise) => {console.log(reason, promise)
// reason 失败起因,// promise 失败的 Promise
})
如果须要无论胜利和谬误都须要执行则能够用 finally
来实现
ajax('/json1.json')
.then(ret => {console.log('胜利执行这个')
}).catch(err => {console.log("失败执行这个")
})
.finally(function() {console.log("胜利和失败都会执行这个")
});
Promise 静态方法
Promise.resolve
疾速的一个值转化为一个Promise
对象, 这种形式和 new Promise
返回一个值是等价的
Promise.resolve({data: "hahah"})
new Promise((resolve) => {
resolve({data: "hahah"})
})
如果传入的是一个 Promise
对象会一成不变的把这个对象返回
function ajax (url) {return new Promise((resove, reject) => {var xhr = new XMLHttpRequest()
xhr.open('GET', url)
// 新办法能够间接承受一个 j 对象
xhr.responseType = 'json'
xhr.onload = function () {if (this.status === 200) {resove(this.response)
} else {reject(new Error(this.statusText))
}
}
xhr.send()})
}
let promise1 = ajax('/url')
let promise2 = Promise.resolve(promise1)
console.log(promise1 === promise2) // true
如果传入的是一个对象,并且这个对象也有一个跟 Promise
一样的 then
办法,也就是说这个方也也能够接管到 onFulfilled, onRejected
两个回调,并且能够调用回调传递参数,这种有then
办法的对象实现了一个 thenable
的接口,反对这种对象的起因是因为原生 Promise
还没有被遍及之前,很多时候都是第三方的库实现的Promise
Promise.resolve({then (onFulfilled, onRejected) {onFulfilled('123')
}
}).then(ret => {console.log(ret) // 123
})
Promise.reject
疾速创立一个肯定是失败的 Promise
对象,这个办法的参数就是 Promise
失败的起因
Promise.reject("嘿嘿,这就是谬误的理由").catch(err => {console.log(err) // 嘿嘿,这就是谬误的理由
})
Promise.all
接管一个数组,这些元素都是一个 Promise
对象,这个办法会返回一个全新的 Promise
对象,当外部所有 Promise
的都实现之后 Promise.all
返回的 Promise
对象才会实现。这个时候 Promise.all
返回的 Promise
对象拿到的后果是一个数组,这个数组中蕴含了每一个 Promise
返回的后果。值得注意的是只有数组中的所有 Promise
都胜利了完结了,Promise.all
返回的 Promise
对象才会胜利完结。如果数组中有一个 Promise
失败的完结了,那么 Promise.all
返回的 Promise
对象也会以失败的完结
Promise.all([ajax('/url1'),
ajax('/url2'),
ajax('/url3'),
ajax('/url4'),
]).then(values => {console.log(values)
}).catch(err => {console.log(err)
})
Promise.race
与 Promise.all
办法一样也是接管一个数组,这些元素都是一个 Promise
对象,这个办法会返回一个全新的 Promise
对象,然而与 Promise.all
办法不同的是 Promise.all
是期待所有工作的完结而完结, Promise.race
只会期待第一个完结的工作而完结
const request = ajax('/api/???')
const timeout = new Promise((resolve, reject) => {setTimeout(() => reject('timeout'), 5000);
})
Promise.race([
request,
timeout
]).then(ret => {console.log(ret)
}).catch(err => {console.log(err)
})
下面代码中,如果接口在 5 秒之前接口返回了,那么咱们能够失常的失去返回后果,如果 5 秒还没有返回,那么申请就没有方法把后果返回回来了,因为 timeout
这个 Promise
会在 5 秒后以失败的形式完结,而 Promise.race
就是以第一个完结的 Promise
而完结
Promise.allSettled
与 Promise.all、Promise.race
办法一样也是接管一个数组,这些元素都是一个 Promise
对象,这个办法会返回一个全新的 Promise
对象,与他们不同的是无论这些 Promise
执行是胜利还是失败都是等这些 Promise
都实现了之后才会实现,当有多个彼此不依赖的异步工作胜利实现时,或者总是想晓得每个 promise
的后果时,通常应用它
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// > "fulfilled"
// > "rejected"
Promise.any
与 Promise.race
办法一样也是接管一个数组,这些元素都是一个 Promise
对象,这个办法会返回一个全新的 Promise
对象,不同的是只有有一个 Promise
执行是胜利的就算胜利,只有全副都失败了才会失败。这个全新的 Promise
的 onFulfilled
的回调函数的参数为第一个胜利实现的 Promise
所传递的数据
const alwaysError = new Promise((resolve, reject) => {reject("失败就失败下一个胜利");
});
const two = new Promise((resolve, reject) => {setTimeout(resolve, 30, "我是第二个实现的 Promise");
});
const three = new Promise((resolve, reject) => {setTimeout(resolve, 70, "我是第三个个实现的 Promise");
});
const one = new Promise((resolve, reject) => {setTimeout(resolve, 10, "我是最先实现的 Promise");
});
Promise.any([two, three, alwaysError, one]).then((value) => {console.log(value); // 我是最先实现的 Promise
// 这个 value 是最先实现的 Promise 传递的值也就是 => 我是最先实现的 Promise
})
Promise 执行时序问题
宏工作,微工作
测试执行程序
console.log('global start')
Promise.resolve().then(ret => {console.log('promise')
})
console.log('global end')
// outlog
// 1. global start
// 2. global end
// 3. promise
链式调用多个执行看执行程序
console.log('global start')
Promise.resolve().then(ret => {console.log('promise1')
}).then(ret => {console.log('promise2')
}).then(ret => {console.log('promise3')
})
console.log('global end')
// outlog
// 1. global start
// 2. global end
// 3. promise1
// 4. promise2
// 5. promise3
退出setTimeout
console.log('global start')
setTimeout(() => {console.log('settimeout')
}, 0);
Promise.resolve().then(ret => {console.log('promise1')
}).then(ret => {console.log('promise2')
}).then(ret => {console.log('promise3')
})
console.log('global end')
// 1. global start
// 2. global end
// 3. promise1
// 4. promise2
// 5. promise3
// 6. settimeout
没想到吧,Promise
的异步时序执行长处非凡。举个例子、如果咱们去银行 ATM 办理贷款,办完之后忽然想起要转一笔账,这时候必定会间接办理转账业务,不会到前面从新排队再转账。这个例子中咱们排队就像在 javascipt
中的期待执行的工作一样,咱们队伍中的每一个人都对应着回调回列中的一个工作、。回调队列中工作称之为 宏工作
,而宏工作执行过程中能够长期加上一些额定需要,这些额定的需要能够抉择作为一个新的宏工作进行到队列中排队。下面的setTimeout
就会作为宏工作再次到回调队列中排队,也能够跟咱们刚的例子一样作为当前任务的 微工作
间接在当前任务完结之后立刻执行。Promise
的回调会作为微工作执行,会在本轮调用的开端去执行,所以说下面代码会先打印 promise1,promise2,promise3
在打印settimeout
微工作
是在起初才被引入到 js
中的,他的目标是为了进步整体的响应能力,目前的绝大多数异步调用都是作为宏工作执行。Promise、MutationObserver
和 nodejs
中的process.nextTick
会作为微工作在本轮调用的开端执行
更多内容微信公众号搜寻
充饥的泡饭
小程序搜一搜开水泡饭的博客