Promise介绍
Promise 是 JavaScript 的异步操作解决方案,是ES6提出的异步操作解决方案之一。
其为异步操作提供统一接口。它起到代理作用(proxy),充当异步操作与回调函数之间的中介,使得异步操作具备同步操作的接口。Promise 可以让异步操作写起来,就像在写同步操作的流程,而不必一层层地嵌套回调函数。
为什么会有promise?
其实在jquery很早以前就有了十分类似promise的方案,该方案的出现是为了解决回调地狱以及传统回调写法不太符合开放封闭的设计原则而产生的,Promise的出现很好地实现了对扩展开放,对修改封闭的原则,可拓展性和可读性较高。
传统的回调写法
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// ...
});
});
});
});
jQuery异步操作历史
jQuery 1.5版本之前
提出的异步解决方案,以ajax为主:
var ajax = $.ajax({
url: './data.json',
success: function () {
console.log('success 1')
console.log('success 2')
console.log('success 3')
},
error: function () {
console.log('error')
}
})
console.log(ajax) //返回的是一个XHR对象
jQuery 1.5版本
引入deferred对象,deferred英文是延期的意思
var ajax = $.ajax('./data.json')
ajax.done(function () {
console.log('success a')
}).fail(function () {
console.log('fail 1')
}).done(function () {
console.log('success b')
}).fail(function () {
console.log('fail 2')
}).done(function () {
console.log('success c')
}).fail(function () {
console.log('fail 3')
})
console.log(ajax) //返回一个deferred对象
jQuery 1.5版本之后
使用了then关键字,此时,我们可以看到,已经和Promise的写法很像了。
var ajax = $.ajax('./data.json')
ajax.then(function () {
console.log('success 100')
}, function () {
console.log('fail 100')
}).then(function () {
console.log('success 200')
}, function () {
console.log('fail 200')
}).then(function () {
console.log('success 300')
}, function () {
conso3le.log('fail 00')
})
jQuery Deferred的使用
首先,我们看下未使用deferred的情况
var wait = function () {
var task = function () {
console.log('执行完成')
}
setTimeout(task, 2000)
}
wait()
引入deferred后可以使用promise的写法,传入dtd, 成功执行resolve, 失败执行reject
function waitHandle() {
// 定义
var dtd = $.Deferred() // 创建一个deferred对象
var wait = function (dtd) {
var task = function () {
console.log('执行完成')
// 成功
dtd.resolve()
// 失败
// dtd.reject()
}
setTimeout(task, 1000)
// wait 返回
// return dtd 1.5之前返回deferred对象
return dtd.promise() // 1.5之后返回promise
}
// 最终返回
return wait(dtd)
}
deferred的实际使用及API分类
API分类
第一类:dtd.resolve, dtd.reject
第二类:dtd.then, dtd.done, dtd.fail
两者不能混用, then是被动监听执行,resolve等是主动执行
var w = waitHandle() // promise 对象
//以下是1.5之前的写法,返回的是deferred对象
/*w.then(function () {
console.log('ok 1')
}, function () {
console.log('err 1')
})
*/
//以下是1.5之后的jquery promise写法,使用者不能直接使用主动调用的w.reject, w.resolve方法
$.when(w).then(function () {
console.log('ok 1')
}, function () {
console.log('err 1')
})
Promise
Promise标准
promise可以看作一种状态管理
- 总共有三种状态 pending, fulfilled, rejected
- 初始状态是pending
- pending可以变为fulfilled或pending之一
- 状态变化不可逆
promise必须实现then方法
- then()必须可以接收两个函数作为参数
- then()返回的必须是一个promise实例
then 方法可以接受两个回调函数,第一个是异步操作成功时(变为fulfilled状态)时的回调函数,第二个是异步操作失败(变为rejected)时的回调函数(该参数可以省略)。一旦状态改变,就调用相应的回调函数。then对上一步的结果负责,then中的resolve在上一步resolve和reject都成功时执行,reject在上一步resolve和reject任意一个失败时执行,可以进行链式调用
Promise解决了什么问题?
使用Promise其实并没有改变JS异步和单线程的本质,只是从写法上杜绝了callback这种形式。Promise是一种语法糖,其解构了代码,更好地符合了开放封闭原则。
一般现代浏览器都支持了Promise,若不支持可以使用bluebird插件
Promise的写法
function loadImg(src) {
var promise = new Promise(function (resolve, reject) {
var img = document.createElement('img')
img.onload = function () {
resolve(img)
}
img.onerror = function () {
reject('图片加载失败')
}
img.src = src
})
return promise
}
Promise的异常捕获
promise.then最后加上.catch,可以统一捕获throw的系统错误以及reject中的信息
var result = loadImg(src)
result.then(function (img) {
console.log(1, img.width)
return img
}).then(function (img) {
console.log(2, img.height)
}).catch(function (ex) {
// 统一捕获异常
console.log(ex)
})
Promise其他api, all与race
all需要传入多个promise实例,全部实例完成后再继续,而race只需其中一个完成即可继续
async await
promise中的then只是将callback进行了拆分。
更进一步可以使用asyn 结合await使用同步的方式写异步的callback. 其是对promise的进一步封装,对写法上的封装。改变不了JS单线程、异步的本质
- 使用方法
在return promise对象的函数前加await
可以获取promise执行后的结果,而不是返回promise对象
const load = async function() {
const result1 = await loadImg(src1)
console.log(result1)
const result2 = await loadImg(src2)
console.log(result2)