Promise是什么?怎么用?

591 阅读4分钟
  • promise是为了更好的管理异步操作的一种技术,有了它可以让异步请求代码更已读,更易写

为什么要用Promise

  • 很多请求必须依赖上一个请求的数据,在没有promise的情况下,我们必须在上一个请求的回调函数的成功回调中,开始新的请求,依次往下,必须层层嵌套,像洋葱包裹。这让代码显得很乱。比如下面:
$.ajax({
        type:"get",
        url:"data/all_goods.json",
        async:true,
        success:function(data){
            //进行ajax的嵌套
            $.ajax({
                type:"get",
                url:"data/friut_goods.json",
                async:true,
                success:function(data){
                    .......
                }

             });
        }

    });

如何理解Promise

  • Promise的三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)
  • resolved(已定型)包括 fulfilled 和 rejected,但是一般情况下指的是成功
//我们可以先写一个 普通的函数test,生成一个随机数,大于1调用resolve。小于零调用reject
function test(resolve, reject) {
    var timeOut = Math.random() * 2;
    console.log('set timeout to: ' + timeOut + ' seconds.');
    setTimeout(function() {
        if (timeOut < 1) {
            console.log('call resolve()...');
            resolve('200 OK');
        } else {
            console.log('call reject()...');
            reject('timeout in ' + timeOut + ' seconds.');
        }
    }, timeOut * 1000);
}

//新建一个promise对象,并传入回调函数
var p = new Promise(test)

p.then(function(date) {
	console.log(date)
}).catch(function(date) {
	console.log(date)
})

多异步操作的链式调用

  • 当我们只new一个promise对象的时候,我们没有调用,我们传进去的函(setTimeout)数就执行了,所以我们通常把它放到一个函数中,需要的时候再调用
  • 为了链式调用我们这样,在每次then的回调函数中,我们return 一个新的promise任务,这样就可以继续.then()下去

//模拟下载数据
function requestURL(url){
	
	//创建promise  resolve解决   reject拒绝
	var promise = new Promise(function(resolve,reject){
		var b = true;//模拟请求是否成功
		
// 	   if(url == "http://getUserList"){//模拟请求http://getUserList时发生错误
// 				b = false;
// 		}
		
		setTimeout(function(){
			
			if(b == true){//如果请求成功
				resolve(url + "-成功");
			}else{//请求失败
				reject(url + "-失败");
			}
			
		},1000);
	});
	
	console.log(promise);
	
	return promise;
}

//step1:调用requestURL函数 - 返回promise对象
//step2:通过.then传入一个匿名函数,成功回调
//step3:通过.catch传入匿名函数,失败回调

requestURL("http://login").then(function(data){
	console.log("data = "+data);
	
	return requestURL("http://getUserList");//返回getUserList请求的promise
}).then(function(data){
	console.log("data = "+data);
	
	return requestURL("http://sendMessage");//返回sendMessage请求的promise
}).then(function(data){
	console.log("data = "+data);
	
	console.log("全部的请求成功!");
}).catch(function(error){
	console.log("发生错误 error = " + error);
});

all race用法

  • all用法,无论顺序如何当所有的异步过程都完成后执行.then的回调函数
  • race用法,当所有的其中一个执行完毕就调用then()的回调函数
//all用法,无论顺序如何当所有的异步过程都完成后执行.then的回调函数
Promise.all([runAsync1(),runAsync2(),runAsync3()]).then(function(){//注意all里面的数据是数组
	console.log("三个异步操作都完成");
})



//race用法,当所有的其中一个执行完毕就调用then()的回调函数
Promise.race([runAsync1(),runAsync2(),runAsync3()]).then(function(){//注意all里面的数据是数组
	console.log("最快的异步操作完成");
})




function runAsync1() {
	var p = new Promise(function(resolve, reject) {
		//做一些异步操作
		setTimeout(function() {
			console.log('异步任务1执行完成');
			resolve('随便什么数据1');
		}, 1000);
	});
	return p;
}

function runAsync2() {
	var p = new Promise(function(resolve, reject) {
		//做一些异步操作
		setTimeout(function() {
			console.log('异步任务2执行完成');
			resolve('随便什么数据2');
		}, 2000);
	});
	return p;
}

function runAsync3() {
	var p = new Promise(function(resolve, reject) {
		//做一些异步操作
		setTimeout(function() {
			console.log('异步任务3执行完成');
			resolve('随便什么数据3');
		}, 2000);
	});
	return p;
}

一个应用:让代码延迟一秒

错误的写法

让接下来的代码延迟一秒再运行,我见过这么写的。。。

setTimeout(function(){
	console.log('延迟一秒');
}, 1000);
// 。。。想要延迟执行的代码
console.log('一秒之后');

显然这大错特错,js的异步代码总是在同步代码运行结束之后执行,所以想要延迟执行的代码反而先执行了

正确的写法

setTimeout(function(){
    // 。。。想要延迟执行的代码
	console.log('一秒之后');
}, 1000);

显然如果逻辑不是很复杂这已经足够了

用Promise封装

正常看来,这样麻烦的封装一个代码延迟不免有杀鸡用牛刀的感觉。但是们要学习promise,而setTimeout是最基本的异步操作,我们就用promise封装一下 因为正常情况下setTimeout只有成功没有失败,简单起见只写成功

var oneSec = new Promise(function(resolve){
	setTimeout(function(){
		console.log('setTimeout 在promise内执行完成');
		resolve('timeOut OK');
	}, 1000)
})

动图我们可以发现,我们new一个promise的过程中,promise中的异步代码已经执行。 而且一开始打印的oneSec是undefined,执行完成之后是一个resolve状态的promise

接下来我们用then接受,在then的回调里写延迟的代码。

var oneSec = new Promise(function(resolve){
	setTimeout(function(){
		console.log('setTimeout 在promise内执行完成');
		resolve('timeOut OK');
	}, 2000)
});
oneSec.then(() => {
	console.log('成功延迟')
})

最后我们用async await 进行延迟

var oneSec = new Promise(function(resolve){
	setTimeout(function(){
		console.log('1.setTimeout 在promise内执行完成');
		resolve('3.timeOut OK');
	}, 2000)
});

async function f(){
	var res = await oneSec;
	console.log("2.两秒以后");
	console.log(res);
}
f();

我们可以看到 await 返回的值,是resolve() 传出的,相同的then()回调函数参数也是这个数据。