Promise
是 ES6 推出的另一个用于异步函数回调的构造函数.
它的使用方式大致如下.
var p = new Promise(function(reslove,reject){
if (!err) reslove(data)
reject(err)
})
p.then((data)=>{
console.log(data) // success
},(err)=>{
console.log(err)
})
一个实际的例子
function promiseGet(url) {
return new Promise(function(reslove,reject){
let xhr = new XMLHttpRequest()
xhr.onload = function(data) {
resolve(json.parse(xhr.responseText))
}
xhr.onError = function() {
reject(xhr.statusText)
}
xhr.open('GET',url,true)
xhr.send()
})
}
promiseGet('./data/users/1')
.then(
data=>{
console.log(data)
},
err=>{
console.log(err)
})
Promise 基本用法说明
我们可以将 Promise
想象成一个容器,这个容器里装一个异步操作的行为.并根据这个异步操作的返回的结果,来告知 Promise
那种是成功状态,哪一种是失败状态.
new Promise(function(resovle,reject){
$.get('./data/users/1',function(data){
if (!data.err) reslove(data.info)
reject(data.err)
})
})
容器里装的异步行为是
$.get()
$.get(,callback) 告知了
Promise
当data.err不存在或者为undefined
的时候,是成功状态.调用reslove
函数,并传入data.info
$.get(,callback) 告知了
Promise
当data.err
存在的时候是失败状态,调用reject
,并传入错误信息.
到目前为止,Promise
接受了一个 函数
作为参数.
此参数函数里,有两个回调函数.
但这些都只是形参,除了函数内部的异步操作.
Promise没有拿到其他任何实质性的数据.
then又是什么?
现在,Promise有了一个异步的操作,它知道需要去执行异步操作了.
我们写代码的时候是 new Promise(function(reslove,reject){....})
传递给 Promise
构造函数的参数函数的 reslove
和 reject
的两个形参,在什么位置赋值呢?
毕竟 Promise
至少在其内部的某个位置,执行了这个 带有异步任务的函数了
// 伪代码
class Promise {
constructor(fn) {
super()
this.fn = fn // 接受到了我们传递的构造函数参数
}
someMoment() {
this.fn(reslove???,reject???)
}
}
then方法出场
我们在创建 Promise
对象的时候,传递了一个 fn
.
Promise
到目前为止起码有一个 异步函数 fn
.
假设我不知道 fn的两个形参如何去传的
.那么,如果我没有传递这两个参数,Promise
接收到了这个没有设置实参到形参的异步回调函数时
,会执行异步任务吗?
let pget = new Promise(function (reslove, reject) {
$.get('http://localhost:8000/users/1', function (data) {
console.log(data)
if (!data.err) reslove(data.info)
reject(data.err)
})
})
请求发出去了,也就说说,Promise
一旦创建,即使没有设置,或者说不知道如何设置 reslove
& reject
异步请求仍会在创建 Promise
的时候,同步发出去.
但在传递的异步函数内部 没有设置 reslove
& reject
实参时却没有报错?
难道是?
// reslove & reject 我压根就没传
console.log(`reslove:${reslove}`)
console.log(`reject:${reject}`)
即使,我并没有传递这两个状态对应的回调函数,在打印它们的时候,发现Promise帮我们自动传递了两个对应的function
.
意义在哪呢?
对于一个函数参数,如果我们没有传递的话?
function fn(p1) {
console.log(p1) //undefined
// p1() // p1 is not function
// p1.a // Cannot read property 'a' of undefined
}
fn()
到目前为止有两个疑问:
对于一个没有设置回调处理
reslove or reject
的Promise
. 它直接把异步请求发出去有意义吗?为什么
Promise
会在我们没有设置reslove
&reject
两个回调函数的时候,主动的去把它俩设置成function
? 意义在哪?
这两个疑问暂且放置一边.
Promise.prototype.then 1.0
then
是 Promise
对象的实例方法.
它用来指定 初始化异步任务函数的 reslove,reject
实参.
let pget = new Promise(function (reslove, reject) {
$.get('http://localhost:8000/users/1', function (data) {
console.log(data)
// reslove & reject 我压根就没传
console.log(`reslove:${reslove}`) // function {navie code}
console.log(`reject:${reject}`) // function {navie code}
if (!data) reslove(data)
reject('出错啦!')
})
})
p.then(data=>{
console.log(data)
},err=>{
console.log(err)
})
使用 then
方法,为每一个 Promise
对象设置 reslove
& reject
也就是说:
我们不设定
reslove
&reject
,Promise
会帮我们封装两个在我们看来什么都不做的reslove
&reject
如果我们使用
then
设定了reslove
&reject
,Promise
就使用我们设定的这两个回调函数.所以,不管什么情况,
Promise
都会需要这两个函数.没有就自己创建,有就直接用.
Promise.prototype.then 2.0
到目前为止,我们知道了
使用
new Promise
创建一个Promise
对象-
构造函数里需要传递一个匿名函数参数.
- 包括异步操作
reslove
reject
使用
then
来执行当前这个Promise
在异步操作状态发生改变时的reslove
&reject
Promise
的 then
是可以链式调用的.
因为 then
方法返回的仍然是 Promise
类型.
then方法返回的是原来的那个 Promise对象
还是一个新的 Promise
对象呢?
var p1 = pCreate('step 01')
var p2 = p1.then(data => {
console.log(data)
return 1
})
console.log(p1 instanceof Promise) // true
console.log(p2 instanceof Promise) // true
console.log(p1 === p2) //false
结论:
+ `then` 方法在执行完毕之后,确实又创建了一个 `Promise`对象
+ 每次 `then` 创建的 `Promise` 对象是不相同的.
为什么每次then指向完毕之后都是返回的一个全新的Promise而不是原来的那个Promise?
一般的链式调用里面都是利用return this
来实现的.
个人理解:
这是和
Promise
的特性相关的
-
Promise
主要是做异步任务的. - 异步任务回来之后,就需要知道成功或者失败了.
- 当我们使用
then
指定Promise
成功或者失败的回调之后,这个Promise
的使命就算完成了. - 异步任务不等于事件,比如按钮点击,可以重复执行.
- 当知道成功和失败之后,这个异步任务就算做完了.
- 所以,一个
then
就代表了老的Promise
任务完结,新的Promise
准备就绪.
Promise.prototype.then 3.0
then
的链式调用
function pCreate(stepName) {
return new Promise(function (reslove, reject) {
console.log('in')
setTimeout(function () {
reslove(stepName)
reject(stepName)
}, 100)
})
}
pCreate(0) // 第一个 promise -- pCreate() 返回
.then(data => { //
console.log(data)
return 1
})
.then(data => { // 第二个 promise 由上一个 then 返回
console.log(data)
return 2
})
.then(data => { // 第三个 promise 由上一个 then 返回
console.log(data)
return 3
})
.then(data => { // 第四个 promise 由上一个 then 返回
console.log(data)
})
这段
then
链式调用,一共产生了4个Promise
.除了第一个
Promise
我们手动了使用pCreate()
方法调用(输出in)之外,剩下三个由then
调用的,都是系统内置的Promise
创建方法.上一个
promise.then.reslove
返回的数据被下一个promise.then.reslove
接收到了.也就说,如果在上一个
then
的reslove
里有返回值,那么在这个then
执行完毕并创建下一个promise
同时,也会把return
的这个data
设置成下一个then
的reslove
的第一个实参.这样做的好处,就是可以把一个 promise 最终在
reslove
里产生的数据传递到下一个Promise
的reslove
中去.
一个then干了三件事情
设置自己
promise
的reslove
&reject
创建一个新的
promise
把自己
promise
的reslove
&reject
里的数据传递给新的promise
的reslove
&reject
中.
then既设置了promise的reslove&reject.同时又创建了一个新的promise.接着还可以把自己的数据传递给下一个promise的reslove&reject
Promise.prototype.then 4.0
既然then
的箭头函数可以传递数据到下一个then
的箭头函数里 那么,如果我在then
箭头函数里返回一个新的 promise
呢?
问题就变得奇怪了.
因为then
会创建一个promis
e,我又在它的箭头函数里返回一个新的 promise
.
但不管怎么说,在代码级别,这样做肯定是可以的.
毕竟then里无非是两个箭头函数,表示 reslove,reject.
函数里,我想返回什么就返回什么.
function pCreate(stepName) {
return new Promise(function (reslove, reject) {
setTimeout(function () {
reslove(stepName)
reject(stepName)
}, 1000)
})
}
pCreate('step 01')
.then(data => {
console.log(`data:${data}`)
return pCreate('step 02')
})
.then(data2 => {
console.log(data2) // step 02
console.log(data2 instance of
})
按照上一阶段的理解,上一个then.reslove
里返回了 return 1
,那么下一个then.reslove(data) === 1
.
所以,这里的 data2
应该是 promise
对象才对.
但结果如下:
当上一个
then.reslove
里返回的是一个非promise
对象时,下一个then.reslove(data)
就等于上一个reslove
里的返回值当上一个
then.reslove
里返回的一个promise
对象是(return pCreate('step 02')). 下一个then.reslove(data)
就不是一个promise
对象了.
但我们发现,console.log(data2)
输出的是 step 02
.
就等于是,第二个通过 return pCreate('step 02')
直接设置 then
了一样.
pCreate('step 02')
.then(data=>{
console.log(data) // step 02
})
结论:
当上一个
then.reslove
中返回的是一个promise
对象时.这个
promise
对象直接就用做成下一个then
的函数调用者所以,下一个
then.reslove(**data**)
就是上一个promise
成功时调用reslove(**data**)
传送的data
.所以,在我们使用
console.log(data2)
时 ,data2 instanceof Promise:false
&data2:step 02
Promise.prototype.then 5.0
既然可以在 then.reslove
里继续返回 promise
.
那么,我们可以把一些有依耐性的接口使用 then
串联起来.
function pGet(url) {
return new Promise(function (reslove, reject) {
get(url, function (data) {
reslove(data)
})
})
}
function get(url, callback) {
let xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.send()
xhr.onload = function () {
callback && typeof callback === 'function' && callback(JSON.parse(xhr.responseText))
}
}
pGet('http://localhost:8000/users/1')
.then(function(data){
console.log(data)
let userInfo = data
return pGet(`http://localhost:8000/preferences/${userInfo.preferences}`)
})
.then((data)=>{
console.log(data)
let preferences = data
return pGet(`http://localhost:8000/hobbies/${preferences.hobbies}`)
})
.then(data=>{
console.log(data)
let hobbies = data
var hobbiesStr = hobbies && hobbies.values instanceof Array && hobbies.values.join(',')
document.querySelector('h1').innerText = hobbiesStr
})