最早接触Promise的时候是在jquery,当时看的懵懵懂懂,没有去深入研究,只处于知道怎么用的阶段。现在在写微信小程序 的时候发现不能使用ES6中的Promises,才发现自己知识的薄弱,查查资料,深度全面的了解下Promise。
简介 Promise:用来传递异步操作的对象 。作为一个异步编程的一种解决方案而生。Promise的中文是承诺,对未来发生的许诺。成功会怎样,失败了又会返回什么。
其实在很早,Promise就实现了。我接触它的时候是jquery(1.5)中$.Deferred.Promise()创建promise对象,JQuery的ajax返回的就是一个deferred对象(deferred 和Promise很容易搞混)。
为什么选择
因为使用简单,提供统一的接口来操作异步。代码可读性强,使用类似同步操作的方式操作异步过程,避免了地狱般回调函数的层层嵌套。莫急 后面代码有详细例子。
ES6 Promise 而ES6只是将其写进语言标准,统一了用法,并原生的提供了Promise对象。
三种状态 Pending(进行中)、Resolved(已完成)、Rejected(已失败)
Api & 实战
Promise.resolve() // 成功返回
Promise.reject() // 失败返回
Promise.prototype.then()
Promise.prototype.catch()
Promise.all() // 所有的完成 var p = Promise.all([p1,p2,p3]);
Promise.race() // 竞速,完成一个即可
传统callback 回调思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
地狱般嵌套:
asyncFunc1(function(){
//...
asyncFunc2(function(){
//...
asyncFunc3(function(){
//...
asyncFunc4(function(){
//...
asyncFunc5(function(){
//... ...
});
});
});
});
});
或者你觉得可以稍微优雅点的地狱
1
2
3
4
5
6
7
8
9
10
11
12
asyncFunc1(){
setTimeout(asyncFunc2, 2000);
}
asyncFunc2(){
setTimeout(asyncFunc3, 2000);
}
asyncFunc3(){
setTimeout(asyncFunc3, 2000);
}
asyncFunc4(){
console.log('asyncFunc4 complete!');
}
而引用promise的链式处理异步后,代码可读性会强,逻辑会更清晰:
1
2
3
4
5
6
7
8
9
10
function asyncFunc1(){
return new Promise(function (resolve, reject) { //...
})
}
asyncFunc1()
.then(asyncFunc2)
.then(asyncFunc3)
.then(asyncFunc4)
.then(asyncFunc5);
典型错误 (我曾经也犯过这样的错误):
1
2
3
4
5
6
7
8
9
10
11
12
13
new Promise(function(res, rej) {
console.log(Date.now() + " start setTimeout 1");
setTimeout(res, 2000);
}).then(function() {
console.log(Date.now() + " timeout 1 call back");
new Promise(function(res, rej) {
console.log(Date.now() + " start setTimeout 2");
setTimeout(res, 3000);
}).then(function() {
console.log(Date.now() + " timeout 2 call back");
})
});
这样子就陷入无限嵌套的噩梦之中,就不是我们要的promise了。
then函数的onFulfilled(onCompleted)回调函数会返回一个新的Promise变量,你可以再次调用这个新的Promise变量的then函数
但是问题来了,如果多个异步嵌套中有个方法返回错误(reject)了,promise将会如何处理 将要如何处理。看代码:
1
2
3
4
5
6
7
8
var promise3 = new Promise((resolve, reject) => {
reject("this is promise3 reject catch");
}).then((msg) => {
console.log('1' + msg);
}).catch((err) => {
console.log('2' + err);
});
//console: 2this is promise3 reject catch
当如果多异步嵌套中有一个请求返回错误,会直接往外抛,在catch中被拦截,而多嵌套异步请求将停止。
隐式包装Promise:在第一个 then中使用 return来返回一个 string, 而不是用 resolve或者 reject,效果其实是一样的。当然不只是 string 也可以直接 return Object等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var promise = new Promise((resolve, reject) => {
var promise = new Promise((resolve, reject) => {
console.log("promise starts");
setTimeout(() => {
resolve("this is promise resolve");
}, 2000);
});
resolve(promise);
}).then((msg) => {
console.log('msg: ' + msg);
return "promise .then()隐式包装resolved Promise";
}, (err) => {
console.log(err);
}).then((word) => {
console.log(word);
});
------ console.log -------
promise starts
msg: this is promise resolve
promise .then()隐式包装resolved Promise
显示包装Promise:在then中返回一个Promise对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var promise = new Promise((resolve, reject) => {
console.log("promise starts1");
var promise = new Promise((resolve, reject) => {
console.log("promise starts2");
setTimeout(() => {
resolve("this is promise resolve");
}, 2000);
});
console.log("promise starts3");
resolve(promise);
}).then((msg) => {
console.log("promise starts4");
console.log('msg: ' + msg);
return Promise.resolve("promise .then()显式包装resolved Promise");
}, (err) => {
console.log(err);
}).then((word) => {
console.log(word);
});
---- console log ------
promise starts1
promise starts2
promise starts3
undefined
promise starts4
msg: this is promise resolve
promise .then()显式包装resolved Promise
Promise.all的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var promise6 = new Promise((resolve, reject) => {
var promiseArr = [];
for (var i = 0; i < 5; ++i) {
promiseArr.push(new Promise((resolve, reject) => {
console.log(`promise6_${i}starts`);
((index) => {
setTimeout(() => {
console.log(`before promise6_${index} resolved`);
resolve(`this is promise6_${index} resolve`);
}, index * 1000);
})(i);
}));
}
resolve(Promise.all(promiseArr));
}).then((msgArr) => {
console.log(`promise6 all resolved ${msgArr}`);
});
//运行结果
//promise6_0 starts
//promise6_1 starts
//promise6_2 starts
//promise6_3 starts
//promise6_4 starts
//before promise6_0 resolved
//before promise6_1 resolved
//before promise6_2 resolved
//before promise6_3 resolved
//before promise6_4 resolved
//promise6 all resolved this is promise6_0 resolve,this is promise6_1 resolve,this is promise6_2 resolve,this is promise6_3 resolve,this is promise6_4 resolve
异常处理的相关内容比较少 参考自http://www.codesec.net/view/493391.html
兼容性 东西是很好用 但是兼容性太差。比如微信小程序就无法使用。也是因此让重新学习Promise的动机.兼容性如下图:
解决方案(小程序) 用polyFill的解决。也就是自己引用第三方库,类似Q、When.js、 bluebird (26.7kb) 等。而且听说第三方库的效率会更高。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var Promise = require('../libs/bluebird.min') //我用了bluebird.js
function wxPromisify(fn) {
return function (obj = {}) {
return new Promise((resolve, reject) => {
obj.success = function (res) {
resolve(res)
}
obj.fail = function (res) {
reject(res)
}
fn(obj)
})
}
}
module.exports = {
wxPromisify: wxPromisify
}
使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var util = require('../utils/util')
var getLocationPromisified = util.wxPromisify(wx.getLocation)
getLocationPromisified({
type: 'wgs84'
}).then(function (res) {
var latitude = res.latitude
var longitude = res.longitude
var speed = res.speed
var accuracy = res.accuracy
}).catch(function () {
console.error("get location failed")
})
小程序兼容性参考自https://segmentfault.com/a/1190000007392283
Promiseの缺点
无法取消。一旦新建就会执行,没方法取消
当处于Pending(进行中)状态的时候,无法得知最近进展到哪个阶段