在前端开发中,有个很熟悉的词叫作“回调”,在处理一些异步的函数的时候,回调被普遍应用,可是大量用回调来编程,会出现嵌套层级过多,代码风格不规范,不清晰的问题。“Promise/A+规范”是一种很方便的异步编程方式。前端
- 将复杂的异步处理轻松地进行模式化
- 代码更清晰
- 异常处理更方便
- 代码链式操做,爽!
先来一段简单的promise的代码:ajax
var promise = getAsyncPromise("fileA.txt");
promise.then(function(result){
// 获取文件内容成功时的处理
}).catch(function(error){
// 获取文件内容失败时的处理
});
复制代码
经过这个代码,是否是以为promise异步编程很清晰?编程
下面我详细介绍下promise实用的方法。json
var promise = new Promise(function(resolve, reject) {
// 异步处理
// 处理结束后、调用resolve 或 reject
});
复制代码
promise.then(onFulfilled, onRejected)
//resolve(成功)时 onFulfilled 会被调用
//reject(失败)时onRejected 会被调用
//onFulfilled 、 onRejected 两个都为可选参数。
复制代码
promise.catch(onRejected)
复制代码
function asyncFunction() {
return new Promise(function (resolve, reject) { //①
setTimeout(function () {
resolve('Async Hello world'); }, 16); //②
});
}
asyncFunction().then(function (value) { console.log(value); // => 'Async Hello world'
}).catch(function (error) { console.log(error);
});
复制代码
栗子说明:数组
上面的代码也也能够不用catch方法,用then(resolve, reject)的形式(链式操做时最好用catch,能够统一捕捉异常):promise
asyncFunction().then(function (value) { console.log(value);
}, function (error) { console.log(error);
});
复制代码
大体的了解promise处理流程,单独介绍一下promise状态bash
*注意:从Pending转换为Fulfilled或Rejected以后, 这个promise对象的状 态就不会再发生任何变化。异步
当promise的对象状态发生变化时,用 .then 来定义只会被调用一次的函数。async
function getURL(URL) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest(); //栗子就不作ie兼容了
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
}
else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
// 运行示例
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value) {
console.log(value); }).catch(function onRejected(error) {
console.error(error); });
复制代码
栗子说明:异步编程
让咱们在实际中使用一下刚才建立的返回promise对象的函数
getURL("http://example.com/"); // => 返回promise对象
复制代码
为promise对象添加处理方法主要有如下两种:
getURL(URL).then(onFulfilled, onRejected); //纯then写法
getURL(URL).then( onFulfilled).catch( onRejected); // then chatch写法
复制代码
注:通常说来,使用 .catch 来将resolve和reject处理分开来写是比较推荐的作法, 这二者的 区别会在then和catch的区别中再作详细介绍。
//好比 Promise.resolve(42); 能够认为是如下代码的语法糖。
new Promise(function(resolve){
resolve(42);
});
复制代码
在这段代码中的 resolve(42);会让这个promise对象当即进入肯定(即resolved)状态,并将 42 传递给后面then里所指定的 onFulfilled 函数。
Promise.resolve(42).then(
function (value) {
console.log(value) //42
}
);
复制代码
注:Promise.resolve做为newPromise()的快捷方式,在进行promise对象的初始化或者编写 测试代码的时候都很是方便。
Promise.resolve方法另外一个做用就是将thenable对象转换为promise对象。
**thenable对象**:简单来讲它就是一个很是相似promise的东西(*就像咱们有时称具备.length方法的非数组对象为Arraylike同样,thenable指的是一个具备.then 方法的对象*。)
复制代码
最简单的例子就是jQuery.ajax(),它的返回值就是thenable的(由于 jQuery.ajax()的返回值是jqXHRObject对象,这个对象具备 .then 方法)
用Promise.resolve来转换为一个promise对象。变成了promse对象的话,就能直接使用 then 或者 catch等这些在ES6Promises里定 义的方法了。
var promise = Promise.resolve(
$.ajax('/json/comment.json')
) // => promise对象
promise.then(function(value){
console.log(value);
});
复制代码
总结:简单总结一下 Promise.resolve 方法的话,能够认为它的做用就是将传递给它的参数填 充(Fulfilled)到promise对象后并返回这个promise对象。
new Promise(
function (resolve,reject) {
reject(new Error("出错了"));
}
);
// 是Promise.reject(newError("出错了"))的语法糖。
//以下:
Promise.reject(new Error("BOOM!")).catch(
function(error){
console.error(error);
}
);
复制代码
aPromise.then(function taskA(value) {
// task A
}).then(function taskB(vaue){
// task B
}).catch(function onRejected(error){ //最后catch 统一捕捉异常
console.log(error);
});
复制代码
来段代码,看下链式操做的执行流程
function taskA() {
console.log("Task A");
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
复制代码
上面这段代码的执行流程以下图:
注释: .cntch() 能够捕捉 taskA taskB出现的异常,可是 onRejected、finalTask这两个后面没有catch捕捉了,因此这两个函数出现问题不会被捕捉到。
前面例子中的Task都是相互独立的,只是被简单调用而已。这时候若是 Task A 想给 Task B 传递一个参数,那就是在 Task A 中 return 的返回值,会在 Task B 执行时传给它。
function doubleUp(value) {
return value * 2;
}
function increment(value) {
return value + 1;
}
function output(value) {
console.log(value);
}
var promise = Promise.resolve(1);
promise
.then(increment)
.then(doubleUp)
.then(output)
.catch(function(error){
// promise chain中出现异常的时候会被调用
console.error(error);
});
1. Promise.resolve(1); 传递 1 给 increment 函数
2. 函数 increment 对接收的参数进行 +1 操做并返回(经过 return ) 3. 这时参数变为2,并再次传给 doubleUp 函数
3. 最后在函数 output 中打印结果
复制代码
**注:**每一个方法中return的值不只只局限于字符串或者数值类型,也能够是对象或者promise 对象等复杂类型。
Promise.all 接收一个promise对象的数组做为参数,当这个数组里的全部promise对象 所有变为resolve或reject状态的时候,它才会去调用 .then 方法。
// `delay`毫秒后执行resolve
function timerPromisefy(delay) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(delay); }, delay
);
});
}
var startDate = Date.now();
// 全部promise变为resolve后程序退出
Promise.all([
timerPromisefy(1),
timerPromisefy(32),
timerPromisefy(64),
timerPromisefy(128)
]).then(function (values) {
console.log(Date.now() - startDate + 'ms'); // 約128ms
console.log(values); // [1,32,64,128]
}
);
复制代码
**注:**从总用时120ms来看,传递给Promise.all的promise并非一个个的顺序执行的,而是 同时开始、并行执行的。
它的使用方法和Promise.all同样,接收一个promise对象数组为参数
Promise.all 在接收到的全部的对象promise都变为 FulFilled 或者 Rejected 状态以后才会继续进行后面的处理,与之相对的是Promise.race 只要有一个promise对象进入FulFilled或者Rejected状态的话,就会继续进行后面的处理。
Promise.race([
timerPromisefy(1),
timerPromisefy(32),
timerPromisefy(64),
timerPromisefy(128)
]).then(function (value) {
console.log(value); // => 1 只打印出一个最早出结果的
});
复制代码