Promise已经出现很久了,但鉴于很多初学者还不是很了解也经常有人问到这个,因此就想写一篇文章来讲解一下这方面的知识,本文会尽量用一些简单的实例来说明一些问题,也会尽量做到通俗易懂、简单明了。
参考文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
一、什么是Promise
Promise是ES6原生提供的一个全局的构造函数,它提供了很多方法供我们使用,它解决了异步操作流程中回掉函数层层嵌套的问题。
当然,看完上面的解释,相信不会有几个人能真正看懂(大神除外)。少一份套路,多一份真诚,牛逼还是要吹的,但要把握一个度,接下来我们用一个简单的实例来看下:
//先实例化一个Promise
var pro=new Promise(function(resolve,reject){
var name="李";
resolve(name);//将name传递下去
});
pro.then(function(data){
//输出成功传来的数据
console.log(data);
//先处理数据
var newData1=data+"可";
//将处理后的数据传递下去
return Promise.resolve(newData1);
}).then(function(data){
//输出成功传来的数据
console.log(data);
//先处理数据
var newData2=data+"可";
//将处理后的数据传递下去
return Promise.resolve(newData2);
}).then(function(data){
//输出最终的数据
console.log(data);
//故意抛出一个异常
throw "Promise不想和你说话,并向你抛出了一个异常!!"
}).catch(function(mes){
//这里处理被拒绝或发生异常的情况
//mes参数代表被拒接或异常时携带的信息
console.log(mes);
});
/* 输出结果如下 */
//李
//李可
//李可可
//Promise不想和你说话,并向你抛出了一个异常!!
看着是不是很牛逼也很神奇啊
,哈哈,我猜肯定没有,这写的啥啊,屁用都没有是吧?肯定是的。但是先别急,上面的代码只是让大家先对Promise有个初步的了解,实际应用的时候肯定不会这么简单啊。另外,这里如果还是没搞懂Promise到底是个什么东西也不要着急,毕竟搞对象一直都不是什么容易的事儿,而何况是搞一个这么牛逼的对象是吧。
下面我们就来详细地逐个讲解每个API
二、then和catch方法
then和catch是Promise.prototype上的两个最常用的方法,下面我们将 从一个最简单的实例开始:
var p1 = new Promise(function(resolve, reject) {
resolve("恭喜你,你赢了");//将来传递给下一层的数据就是里面的参数
});
p1.then(function(data) {
var num=Math.random();//生成一个随机数
if(num>0.1){ //用户有90%的可能性会赢
console.log(data);
return;
}
throw "厉害了word哥,这都能输!";
}).catch(function(e) {
console.log(e);//如果输了,则会被嘲讽
}); 1.在这里,我们首先实例化了一个Promise,并将一个函数作为参数传入进去,函数中传入了两个参数resolve和reject,需要注意的是这两个参数都是函数,使用方法是resolve(data)和reject(data),参数data可以是任意数据类型,参数data代表了将要传递下去的数据。
三、Promise.resolve()和Promise.reject()
new Promise(function(resolve,reject){
resolve(1);
}).then(function(data){
//返回一个Promise的实例
return new Promise(function(resolve,reject){
resolve(data+1);
})
}).then(function(data){
//返回一个Promise的实例
return new Promise(function(resolve,reject){
resolve(data+1);
})
}).then(function(data){
//返回一个Promise的实例
return new Promise(function(resolve,reject){
resolve(data+1);
})
}).then(function(data){
console.log(data);
})
//4 哈哈,恭喜某些同学,还真可以,但是我们刚开始就说了 " 它解决了异步操作流程中回掉函数层层嵌套的问题 ",然而我们现在却已经嵌套了三层,虽然嵌套的不是很多,但是我们追求的是完美不是吗?如果还有更复杂的需求那该怎么办,我们岂不是深陷其中而无法自拔
。
Promise.resolve(value)方法返回一个以给定值解析后的Promise实例。但如果这个值是个thenable(即带有then方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态(指resolved/rejected/pending/settled);否则以该值为成功状态返回promise对象。
Promise.resolve(1)//1
.then(function(data){
return Promise.resolve(data+1);//2
}).then(function(data){
return Promise.reject(data+1+"时被拒绝");//3
}).then(function(data){
return Promise.resolve(data+1);//4
}).then(function(data){
console.log(data);//结果
}).catch(function(mes){
console.log(mes);//输出拒绝的"原因"
});
//3时被拒绝 我们说了,Promise.resolve会返回一个Promise实例,因此上面的代码也就不难理解了,每次都return一个Promise.resolve,而Promise.resolve又会返回一个Promise实例,因此我们可以接着再后面跟"then",从而实现了多级链式调用。
/*****************1******************/
var original = Promise.resolve(true);
var cast = Promise.resolve(original);
cast.then(function(v) {
console.log(v); // true
});
/*****************2******************/
var original = new Promise(function(resolve,reject){
resolve(true);
});
var cast = Promise.resolve(original);
cast.then(function(v) {
console.log(v); // true
}); Promise.resolve()的特殊用法2:传入一个特殊的对象(含有then方法),例如下面的代码
/***********************************1************************************/
// Resolve一个thennable对象
var p1 = Promise.resolve({
then: function(resolve, reject) { resolve("fulfilled!"); }
});
console.log(p1 instanceof Promise) // true, 这是一个Promise对象
p1.then(function(v) {
console.log(v); // 输出"fulfilled!"
}, function(e) {
// 不会被调用
});
/**********************************2************************************/
// Thenable在callback之前抛出异常
// Promise rejects
var thenable = { then: function(resolve) {
throw new TypeError("Throwing");
resolve("Resolving");
}};
var p2 = Promise.resolve(thenable);
p2.then(function(v) {
// 不会被调用
}, function(e) {
console.log(e); // TypeError: Throwing
});
/************************************3*************************************/
// Thenable在callback之后抛出异常
// Promise resolves
var thenable = { then: function(resolve) {
resolve("Resolving");
throw new TypeError("Throwing");
}};
var p3 = Promise.resolve(thenable);
p3.then(function(v) {
console.log(v); // 输出"Resolving"
}, function(e) {
// 不会被调用
}); 细心的小伙伴可能就会发现其实传入thenable(含有then方法的对象)和传入一个Promise的实例,效果基本上是一样的,其用法和传入一个new Promise(function(resolve,reject){})也非常类似,刚开始我们只需要习惯一种用法即可。
四,Promise的其它写法
/******************1******************/
new Promise(function(res,rej){
rej("err1");
}).then(function(){
},function(err){
//捕获到错误
console.log(err);
})
/*******************2*****************/
Promise.reject("err1").then(function(){
},function(err){
//捕获到错误
console.log(err);
})
/*******************3*****************/
Promise.resolve(1)
.then(function(data){
throw new TypeError("err1")
}).then(function(){
},function(err){
//捕获到错误
console.log(err);
}); 总之,then可以有第二个参数,这个参数是一个函数,用来捕获上一层可能会传来的错误。
五、Promise的其它静态方法
-
Promise.all()
请原谅我比较懒,再次复制MDN的解释,Promise.all(iterable)方法返回一个promise,该promise会等iterable参数内的所有promise都被resolve后被resolve,或以第一个promise被reject的原因而reject 。
接下来还是先看几个实例吧:/*****************1*********************/ var promise = Promise.resolve(3); Promise.all([true, promise]) .then(values => { console.log(values); // [true, 3] }); /*****************2*********************/ var arr=[1,Promise.reject("err"),Promise.resolve(3)]; Promise.all(arr) .then(values =>{ console.log(values);//不会执行 }).catch(err => { console.log(err);//err }); /*****************3*********************/ var arr=[1,Promise.reject("err"),Promise.resolve(3)]; Promise.all(arr) .then(values =>{ console.log(values);//不会执行 },err => { console.log(err);//err });总结一下,Promise.all的参数是一个数组,这个数组里面的元素可以是Promise的实例,也可以不是,如果不是(比如传入基本数据类型),则会被自动转换为一个Promise对象,最终会在then的第一个参数里得到一个数组,这个数组里面的每个值对应着当时传入的数组里每个元素(是一个Promise的实例)resolve的值。如果传入数组里面的某一个元素被reject了,那么后面将会立即捕获到这个reject的原因,并且不再理会其它传入的promise是否被resolve。 -
Promise.race()
Promise.race(iterable)方法返回一个promise,这个promise在iterable中的任意一个promise被解决或拒绝后,立刻以相同的解决值被解决或以相同的拒绝原因被拒绝。其中iterable是一个存放着若干个Promise的数组。iterable内的每一个Promise的实例就像在同一起跑线上的运动员,他们即将进行一场百米赛跑,谁先到达终点谁就会被接纳,而其他运动员则竟被忽略掉。
通俗来讲,
看代码:/***************************1******************************/ var p1 = new Promise(function(resolve, reject) { setTimeout(resolve, 500, "一"); }); var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "二"); }); Promise.race([p1, p2]).then(function(value) { console.log(value); // "二" // 两个都解决,但p2更快 }); /***************************2******************************/ var p3 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "三"); }); var p4 = new Promise(function(resolve, reject) { setTimeout(reject, 500, "四"); }); Promise.race([p3, p4]).then(function(value) { console.log(value); // "三" // p3更快,所以被解决(resolve)了 }, function(reason) { // 未被执行 }); /***************************3******************************/ var p5 = new Promise(function(resolve, reject) { setTimeout(resolve, 500, "五"); }); var p6 = new Promise(function(resolve, reject) { setTimeout(reject, 100, "六"); }); Promise.race([p5, p6]).then(function(value) { // 未被执行 }, function(reason) { console.log(reason); // "六" // p6更快,所以被拒绝(reject了) });
六、实际应用
讲了这么多废话,终于到了实际应用了,对于Promise来说,用到ajax当中是最合适不过了,我们可以使用原生的ajax结合Promise来写一个插件,要求可以链式调用
'use strict';
// A-> $http function is implemented in order to follow the standard Adapter pattern
function $http(url){
// A small example of object
var core = {
// Method that performs the ajax request
ajax : function (method, url, args) {
// Creating a promise
var promise = new Promise( function (resolve, reject) {
// Instantiates the XMLHttpRequest
var client = new XMLHttpRequest();
var uri = url;
if (args && (method === 'POST' || method === 'PUT')) {
uri += '?';
var argcount = 0;
for (var key in args) {
if (args.hasOwnProperty(key)) {
if (argcount++) {
uri += '&';
}
uri += encodeURIComponent(key) + '=' + encodeURIComponent(args[key]);
}
}
}
client.open(method, uri);
client.send();
client.onload = function () {
if (this.status >= 200 && this.status < 300) {
// Performs the function "resolve" when this.status is equal to 2xx
resolve(this.response);
} else {
// Performs the function "reject" when this.status is different than 2xx
reject(this.statusText);
}
};
client.onerror = function () {
reject(this.statusText);
};
});
// Return the promise
return promise;
}
};
// Adapter pattern
return {
'get' : function(args) {
return core.ajax('GET', url, args);
},
'post' : function(args) {
return core.ajax('POST', url, args);
},
'put' : function(args) {
return core.ajax('PUT', url, args);
},
'delete' : function(args) {
return core.ajax('DELETE', url, args);
}
};
};
// End A
// B-> Here you define its functions and its payload
var mdnAPI = 'https://developer.mozilla.org/en-US/search.json';
var payload = {
'topic' : 'js',
'q' : 'Promise'
};
var callback = {
success : function(data){
console.log(1, 'success', JSON.parse(data));
},
error : function(data){
console.log(2, 'error', JSON.parse(data));
}
};
// End B
// Executes the method call
$http(mdnAPI)
.get(payload)
.then(callback.success)
.catch(callback.error);
// Executes the method call but an alternative way (1) to handle Promise Reject case
$http(mdnAPI)
.get(payload)
.then(callback.success, callback.error);
// Executes the method call but an alternative way (2) to handle Promise Reject case
$http(mdnAPI)
.get(payload)
.then(callback.success)
.then(undefined, callback.error);
$http(mdnAPI)
.get(payload)
.then(function(data){
console.log(data);
//这里可以继续return一个请求的发送
//例如return $http(mdnAPI).get(payload)
//内部会return一个Promise的实例,因此下面就可以继续"then"
})
.catch(callback.error);
七、Promise的兼容性


本文深入浅出地介绍了JavaScript中的Promise概念及其用法,通过多个示例展示了如何利用Promise解决异步编程中的回调地狱问题,并提供了实际应用案例。
1275

被折叠的 条评论
为什么被折叠?



