js promise对象

promise对象是javaScript的异步操作解决方案,,为异步操作提供统一的接口。它起到代理的作用(proxy),充当异步操作与回调函数之间的中介,使得异步操作具备同步操作的接口。Promise可以让异步操作写起来,就像在写同步操作的流程,而不必一层层的嵌套回调函数。

Promise的设计思想是,所有异步任务都返回一个Promise实例,Promise实例有一个then方法,用来指定下一步的回调函数。

var asyncFn = function(){
    return new Promise(function(resolve,reject){
        //异步操作··· ···
    })
}
asyncFn.then(function(){})

Promise对象的状态:

promise对象通过自身的状态,来控制异步操作。Promise实例有三种状态:

     · 异步操作未完成(pending)

     · 异步操作成功(resolved)

     · 异步操作失败(rejected)

这三种的状态的变化途径只有两种:

     ·  从“未完成”到“成功”

     ·  从“未完成”到“失败”

一旦状态发生变化,就凝固了,不会再有新的状态变化。这就是Promise名字的由来,它的英文意思是“承诺”,一旦承诺成效,就不得改变了。

  因此,Promise的最终结果只有两种。

      · 异步操作成功,Promise实例传回一个值(value),状态变为resolved。

      · 一步操作失败,Promise实例抛出错误(error),状态变为rejected。 

Promise构造函数:

Promsie构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,他们是两个函数,由js引擎提供,不用自己实现。

resolve函数的作用是 —— 将Promise实例的状态从“未完成”变为”成功”(既从pending变为fulfilled),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。

reject函数的作用是—— 将Promise实例的状态从“未完成”变为“失败”(即从pending变为rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

eg:

var asyncFn = function(){
    return new Promise(function(resolve,reject){
        setTimeout(function(){
            resolve("成功")
        },2000)
    })
}
asyncFn().then(function(getParam){
    console.log(getParam);//成功
})

上边代码:2秒后打印“成功”,,,Promise实例的状态从 “未完成” 变为“成功”,(既从pending变为fulfilled),执行resolve函数,并把参数传递给回调函数。

Promise.prototype.then():

then方法可以接受两个回调函数,第一个是异步操作成功时(变为resolved状态)的回调函数,第二个是异步操作失败(变为rejected)时的回调函数(该参数可以省略)。一旦状态改变,就会调用相应的回调函数。

eg:来看下一个图片加载的小例子

var imgOnload =function(path){ 
    return new Promise(function(resolve,reject){
        var myImg = new Image();
        myImg.onload = resolve;
        myImg.onerror = reject;
        myImg.src = path;
    })
};
imgOnload("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561950592783&di=2cf2061e2eab1de893a8cf80b0f6181b&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20171117%2F9ad732c1a18c4bd487943c8b3884a7b4.gif")
    .then(function(e){
    console.log(e)//Event {isTrusted: true, type: "load", target: img, currentTarget: img, eventPhase: 2, …}
        $("body").append(e.target)
    },function(error){
        console.log(error);
    })

上边代码:在Promise参数函数中,我们创建了一个图片对象实例,它有两个事件监听属性,onload:图片加载成功后调用;

onerror:属性在加载失败调用。当图片加载成功后,onload属性会返回一个事件对象,所以我们通过then方法的回调函数,会接收到这个事件对象,该对象的target属性就是图片加载成功后生成的Dom节点。。。

Promise.prototype.catch():

用于指定错误发生时的回调函数;

new Promise(function(resolve,reject){
	throw new Error("语法错误");
}).catch(function(error){
	console.log(error)//Error:语法错误
})

上边代码,promise跑出一个错误,被catch方法指定的回调函数捕获。上边我们提到的reject方法跟它是一样的,也是抛出一个错误。

如果Promsie状态已经变成resolved,在抛出错误是无效的:

new Promise(function(resolve,reject){
	resolve("成功");
	throw new Error("语法错误");
}).then(function(param){
	console.log(param);//成功
}).catch(function(error){
	console.log(error)//无返回
})

上边代码,在Promise参数函数中,resolve后边抛出一个错误,下边catch未捕获到,。因为Promise的状态一旦改变,就永久保持该状态,不会变了。

练习题:

1、下边输出结果为?输出promise2的状态刚开始是?

const promise = new Promise((resolve, reject) => {
    throw 1
})
const promise2 = promise.then((res) => {
    console.log('res:', res)
    return res
}).catch(err => {
    console.log('err', err)
    return err
})
console.log('status:', promise2)

解答:

第一问:结果很容易,抛出是1,所以走的是catch,打印结果:'err:' 1

第二问:输出的状态刚开始一定为pedding,这个一点毛病都没有,当catch里边的异步函数执行完成时,此时的状态决定promise2的状态,如果抛出异常,promise2的状态为rejected;如果没有异常,promise2的状态为resolved。

 

2、解释下边2段代码,为什么输出的promise2的状态结果不同?

const promise = new Promise((resolve, reject) => {
    throw 1
})
const promise2 = promise.then((res) => {
    return res
}).catch(err => {
    throw err
})
console.log('status:', promise2) // 'rejected'

 

const promise = new Promise((resolve, reject) => {
    throw 1
})
const promise2 = promise.then((res) => {
    return res
}).catch(err => {
    setTimeout(() => {
        console.log('err', err)
        throw err
    }, 2000)
})
console.log('status:', promise2) // 'resolved'

解答:

整明白上边的代码,必须明白:(1)如果没有异常,promise返回到状态都是resolved;(2)状态一旦凝固(状态已定),后边将不可能修改其状态。

所以上边最后一段代码,catch中虽然有一个异常抛出,但是毕竟setTimeout是一个异步函数,所以catch默认返回的是resolved状态,2秒钟之后,即使抛出异常也不能够修改其状态了,因为状态早已经凝固(定)了。

 

Promise.prototype.finally():

finally方法用于指定不管Promise对象最后的状态如何,都会执行的操作。该方法是ES2018引入的标准。

var promise = new Promise(function(resolve,reject){
    resolve("成功");
})
promise.then(function(param){
    console.log(param)//成功
}).finally(function(){
    console.log("执行完毕")//执行完毕
})

补充:

1、resolve( ) / reject(): 直接返回resolve( ) 或 reject()

相当于

Promise.resolve( ) / Promise.reject( )

在实际开发中的例子(uni-app为例):

下边其实是封装了一个发送数据的方法,里边有好多逻辑,只不过是简化了,判断后台返回数据中的code码。

const request = (param) => {
  return new Promise((resolve, reject) => {
	// 请求数据
    let requestTask = uni.request({
	    url: 'www.xxx.com',
		method: 'GET',
		data: { ··· }
		success: (res) => {
			if (res.data.result.status === -1) {
				reject();
			} else if (res.data.result.status === 0) {
				reject();
			}
			resolve(res.data.result); // 返回后台传过来的数据
		},
		fail (e) {
			reject(e)
		}
	}
  })
}

2、promise.all():参数是一个装有一个或多个promise对象的数组

(1)下边例子中,all后边的打印输出,要等等到所有promise都执行完之后才能输出

const promiseAll = []
for(let i=0; i<10; i++) {
    promiseAll.push(new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(i)
        }, 1000)
    }))
}
Promise.all(promiseAll).then((res) => {
    console.log(res) //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
})

 (2)实际开发中的一个例子(小程序云开发):

下边这个是在小程序云开发中的一个真实例子,小程规定:每次从云数据库中获取的数据是有限制的,100条,需求是必须要从云数据库中获取到全部数据,与后台返回的数据去比对,如果后台返回到数据与云数据库的数据不同才会存到云数据库中去:

async function getCloudDatabaseAllData(dbData) {
  // 参数dbData就是await db.collection('playlist').count() 获取云数据库中数据的数量方法
  const MAX_COUNT = 100 // 云数据库每次取数据最多只能取100条
  const _totals = dbData.total // 获取云数据库中数据总的数量
  const times = Math.ceil(_totals / MAX_COUNT) // 获取循环次数
  const tasks = [] // 作为Promise.all的参数,每次请求的数据 [{}, {}, ··· ···]
  for(let i = 0; i < times; i ++) {
    const getdbDataPromise = db.collection('playlist').skip(i * MAX_COUNT).limit(MAX_COUNT).get() // 从第几位开始取数据,一次取多少个数据
    tasks.push(getdbDataPromise) // 把取到的数据push到tasks中去
  }
  const res = await Promise.all(tasks) // [{}, {}, {}]
  return res.reduce((prev, next) => {
    return {
      data: prev.data.concat(next.data) // 这里取到的数据是一个对象,对象中的data才是我们真正想要的数据
    }  
  })
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值