首先,先來看Promise用法,Promise主要用于異步,在之前的JS,除了上傳圖片,以及Ajax,幾乎沒有要用到異步的地方。但是Node.JS的出現(xiàn),改變了這一現(xiàn)象,因為在Node出現(xiàn)的時候,后端語言,Java,Python,PHP已經(jīng)占據(jù)了后端的主要份額,要想脫引而出,就要有一些其他語言沒有的特點,Node的特點,就是大量回調(diào)來處理并發(fā), 所以會用到大量的回調(diào)函數(shù),Promise就登場了,當(dāng)然,ES7出現(xiàn)的asyns和await更是將回調(diào)完美的展現(xiàn)出來,這點在Koa2中,大量運用。Promise究竟為何物,我們先來揭開Promise的用法吧:
不用Promise的情況:
const ajax = function(callback){
console.log("start");
setTimeout(()=>{
callback&&callback.call();
},1000)
}
ajax(function(){
console.log("Hello World");
})
我們定義一個ajax方法,里面?zhèn)魅胍粋€方法,用setTimeout模擬發(fā)送請求的操作,然后在一秒鐘,輸出這個方法,結(jié)果也輸出正確。
Promise基本使用:
const ajax = function(){
console.log("start");
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve("hello");
},1000)
})
}
ajax().then((value)=>{
console.log(value,"world");
})
和上文 一樣,只不過這次是返回一個Promise對象,Promise對象可以鏈?zhǔn)秸{(diào)用then方法,進行下一步操作。
Promise鏈?zhǔn)秸{(diào)用:
const ajax = function(){
console.log("start");
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve()
},1000);
})
}
ajax().then(function(){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve()
},2000);
})
}).then(()=>{
console.log("Hello World");
})
注意,then方法,會默認(rèn)返回一個Promise對象,比如return 1,這個1,就對應(yīng)在下次then(value=>{})的value值,但是這樣功能太少了,比如說拋出錯誤,不好維護,所以還是應(yīng)該寫return new Promise();
Promise鏈?zhǔn)秸{(diào)用的復(fù)雜情形:
new Promise(resolve=>{
console.log("start1");
setTimeout(()=>{
resolve(100);
},1000);
})
.then(value=>{
return new Promise(resolve=>{
console.log("start2");
console.log(value);
setTimeout(()=>{
resolve(200);
},1000);
})
})
.then(value=>{
return new Promise(resolve=>{
console.log("start3");
console.log(value);
setTimeout(()=>{
resolve(300);
},1000);
})
})
第一個方法,返回的結(jié)果參數(shù),會被第二個方法執(zhí)行,第二個方法返回的參數(shù)會被第三個執(zhí)行。
Promise報錯的處理:
const ajax = function(num){
console.log("start");
return new Promise(function(resolve,reject){
if(num>5){
resolve();
}else{
throw new Errow("出錯了");
}
})
}
ajax(3).then(()=>{
console.log("Hello World");
}).catch((err)=>{
console.log(err);
})
建議即使只需要then,還是應(yīng)該寫catch,捕獲異常。
Promise高級篇:
所有圖片加載完再添加到頁面:
function loadImg(src) {
return new Promise((resolve, reject) => {
let img = document.createElement('img');
img.src = src;
img.onload = function() {
resolve(img);
}
img.onerror = function(err) {
reject(err);
}
})
}
function showImgs(imgs) {
imgs.forEach(function(img) {
document.body.appendChild(img);
})
}
Promise.all([
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559449634314&di=56a92121182ba40c4a640c053dc0c64b&imgtype=0&src=http%3A%2F%2Fb.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F9825bc315c6034a8ef5250cec5134954082376c9.jpg'),
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559449650753&di=5642d7deef9e1e63b6f3868c75b625f0&imgtype=0&src=http%3A%2F%2Fg.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2Fc2cec3fdfc03924590b2a9b58d94a4c27d1e2500.jpg'),
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559449650745&di=6d65c12f144a14bb2593901da1e995a1&imgtype=0&src=http%3A%2F%2Fh.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F0b46f21fbe096b63491b16ea06338744ebf8ac0e.jpg')
]).then(showImgs)
有一個圖片加載完就添加到頁面:
function loadImg(src){
return new Promise((resolve,reject)=>{
let img=document.createElement('img');
img.src=src;
img.onload=function(){
resolve(img);
}
img.onerror=function(err){
reject(err);
}
})
}
function showImgs(img){
let p=document.createElement('p');
p.appendChild(img);
document.body.appendChild(p)
}
Promise.race([
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559449634314&di=56a92121182ba40c4a640c053dc0c64b&imgtype=0&src=http%3A%2F%2Fb.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F9825bc315c6034a8ef5250cec5134954082376c9.jpg'),
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559449650753&di=5642d7deef9e1e63b6f3868c75b625f0&imgtype=0&src=http%3A%2F%2Fg.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2Fc2cec3fdfc03924590b2a9b58d94a4c27d1e2500.jpg'),
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559449650745&di=6d65c12f144a14bb2593901da1e995a1&imgtype=0&src=http%3A%2F%2Fh.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%210b46f21fbe096b63491b16ea06338744ebf8ac0e.jpg')
]).then(showImgs)
實現(xiàn)隊列:
function queue(things) {
let promise = Promise.resolve();
things.forEach(element => {
promise = promise.then(() => {
return new Promise(resolve => {
setTimeout(() => {
console.log(element)
resolve('ok');
}, 1000);
});
})
});
}
queue(['a', 'b', 'c']);
可依靠這個實現(xiàn)JQuery的animate隊列。
ES7寫法:
async function queue(arr) {
let res = null
for (let promise of arr) {
res = await promise(res)
}
return await res
}
queue(["a", "b", "c"])
.then(data => {
console.log(data)// abc
})
Promise.reduce也可以用來順序執(zhí)行函數(shù),但是可使用的場景非常有限,一般用來讀取文件信息。
手寫Promise實現(xiàn):
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
function MyPromise(fn){
let that = this;
that.state = PENDING;
that.value = null;
that.resolvedCallbacks = [];
that.rejectedCallbacks = [];
function resolve(value){
if(that.state === PENDING){
that.state = RESOLVED;
that.value = value;
that.resolvedCallbacks.map(callback=>callback(that.value))
}
}
function reject(value){
if(that.state === PENDING){
that.state = REJECTED;
that.value = value;
that.rejectedCallbacks.map(callback=>callback(that.value))
}
}
try{
fn(resolve,reject)
}catch(e){
reject(e)
}
}
MyPromise.prototype.then = function(onFulfilled,onRejected){
let that = this;
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : r=>{throw r};
if(that.state === PENDING){
that.resolvedCallbacks.push(onFulfilled)
that.rejectedCallbacks.push(onRejected)
}
if(that.state === RESOLVED){
onFulfilled(that.value);
}
if(that.state === REJECTED){
onRejected(that.value);
}
}
const ajax = function(){
console.log("start");
return new MyPromise(function(resolve,reject){
setTimeout(()=>{
resolve("hello");
},1000)
})
}
ajax().then((value)=>{
console.log(value,"world");
})