本文你將看到:
promise介紹以及用法
promise常用api
demo使用Promise實(shí)現(xiàn)一個(gè)簡(jiǎn)單axios
async方法
1.promise基本用法
Promise 是異步編程的一種解決方案豁延。
從語(yǔ)法上說(shuō),Promise 是一個(gè)對(duì)象,從它可以獲取異步操作的消息锌半。
我們先通過(guò)一個(gè)例子來(lái)談?wù)刾romise。
function dome(t) {
return new Promise((resolve, reject) => {
setTimeout(resolve, t, 'resolve');
});
}
dome(1000).then((value) => {
console.log(value);
})
Promise構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù)寇漫,該函數(shù)的兩個(gè)參數(shù)分別是resolve和reject刊殉。
resolve函數(shù):將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤俺晒Α辈惒讲僮鞯慕Y(jié)果,作為參數(shù)傳遞出去
reject函數(shù):將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤笆 敝硗螅瑢惒讲僮鲌?bào)出的錯(cuò)誤冗澈,作為參數(shù)傳遞出去。
下面我們?yōu)槟阏f(shuō)說(shuō)陋葡,到底發(fā)生了什么:
當(dāng)demo執(zhí)行時(shí)亚亲,新建了一個(gè)promise實(shí)例,并且在t毫秒以后腐缤,執(zhí)行resolve函數(shù)捌归。即表示成功的函數(shù)。
then可以接受兩個(gè)回調(diào)函數(shù)作為參數(shù)岭粤。第一個(gè)代表成功時(shí)調(diào)用惜索,第二個(gè)代表失敗時(shí)調(diào)用。
因?yàn)槲覀冇|發(fā)了resolve剃浇,并且返回‘resolve’巾兆,所以,出發(fā)了then第一個(gè)回調(diào)函數(shù)會(huì)打印value會(huì)打印出 resolve虎囚。
如果角塑,promise實(shí)例即沒(méi)有執(zhí)行resolve也沒(méi)有執(zhí)行reject,則這兩個(gè)回調(diào)函數(shù)都不會(huì)調(diào)用淘讥,例如
function dome(t) {
return new Promise((resolve, reject) => {});
}
dome(1000).then((value) => {
console.log(1);
},(value) => {
console.log(2)
})
運(yùn)行結(jié)果圃伶,將什么也不打印。
如果調(diào)用resolve:
function dome(t) {
return new Promise((resolve, reject) => {
resolve('ok')
});
}
dome(1000).then((value) => {
console.log(value);
},(value) => {
console.log(2)
})
結(jié)果為:
如果調(diào)用reject:
function dome(t) {
return new Promise((resolve, reject) => {
reject('err')
});
}
dome(1000).then((value) => {
console.log(value);
},(err) => {
console.log(err)
})
結(jié)果:
那么promise的執(zhí)行的先后順序是如何呢蒲列?
let promise = new Promise((resolve, reject) => {
console.log('p');
resolve();
});
promise.then( () => {
console.log('ok');
});
console.log('no');
結(jié)果:
2.then方法的鏈?zhǔn)綄?xiě)法
promise實(shí)例調(diào)用then方法以后窒朋,返回的是一個(gè)新的promise實(shí)例。因此可以在then方法的后面繼續(xù)調(diào)用then方法蝗岖。
例如:
function dome(t) {
return new Promise((resolve, reject) => {
setTimeout(resolve, t, t*2);
});
}
dome(1000).then( value => {
return dome(value)
}).then( value => {
console.log('sec'+value)
},err => {
console.log('err'+err)
})
即結(jié)果為
catch方法
Promise.prototype.catch方法是.then(null, rejection)的別名侥猩。
function dome(t) {
return new Promise((resolve, reject) => {
setTimeout(reject, t, '發(fā)生錯(cuò)誤');
});
}
dome(1000).catch( err=> {
console.log(err)
})
結(jié)果:
如果Promise狀態(tài)已經(jīng)變成Resolved,再拋出錯(cuò)誤是無(wú)效的抵赢。
并且Promise 對(duì)象的錯(cuò)誤會(huì)一直向后傳遞欺劳,直到被捕獲為止洛退。也就是說(shuō),錯(cuò)誤總是會(huì)被下一個(gè)catch語(yǔ)句捕獲杰标。
例如:
dome(1000).then( value => {
return dome(value)
}).then( value => {
console.log('sec'+value)
}).catch( err=>{})
上面代碼中,一共有三個(gè)promise對(duì)象:一個(gè)由dome產(chǎn)生彩匕,兩個(gè)由then產(chǎn)生腔剂。它們之中任何一個(gè)拋出的錯(cuò)誤,都會(huì)被最后一個(gè)catch捕獲驼仪。
所以建議使用catch方法掸犬,盡量不要用then方法的第二個(gè)參數(shù),因?yàn)閏atch可以捕獲到前面then方法中的錯(cuò)誤绪爸。
all方法
Promise.all方法可以將多個(gè) Promise 實(shí)例湾碎,包裝成一個(gè)新的 Promise 實(shí)例。
在多個(gè)實(shí)例中奠货,加入有一個(gè)實(shí)例被rejected介褥,則新的實(shí)例就會(huì)變成rejected。
只有當(dāng)所有實(shí)例狀態(tài)都變成fulfilled递惋,則新的實(shí)例狀態(tài)才會(huì)變成fulfilled柔滔。
所有實(shí)例的返回值將組成一個(gè)數(shù)組,返回給新實(shí)例萍虽。
例如
function dome1() {
return new Promise((resolve, reject) => {
resolve('dome1')
});
}
function dome2() {
return new Promise((resolve, reject) => {
resolve('dome2')
});
}
function dome3() {
return new Promise((resolve, reject) => {
resolve('dome3')
});
}
Promise.all([dome1(),dome2(),dome3()]).then((result) => {
console.log(result)
}).catch(err => {
console.log(err)
})
結(jié)果
因?yàn)槊總€(gè)勢(shì)力都是resolve 所以返回了包含每個(gè)實(shí)例返回值組成的數(shù)組睛廊。
function dome1() {
return new Promise((resolve, reject) => {
resolve('dome1')
});
}
function dome2() {
return new Promise((resolve, reject) => {
reject('err2')
});
}
function dome3() {
return new Promise((resolve, reject) => {
resolve('dome3')
});
}
Promise.all([dome1(),dome2(),dome3()]).then((result) => {
console.log(result)
}).catch(err => {
console.log(err)
})
結(jié)果:
第二個(gè)reject所以最終返回了 err
race
race和all方法類似,只不過(guò)在在邏輯上只要所有子實(shí)例中有一個(gè)狀態(tài)發(fā)生改變杉编,就會(huì)立馬把狀態(tài)傳遞給新的實(shí)例超全。在這里就不在寫(xiě)例子了。大家自己研究邓馒。
reject方法與reject方法
Promise.resolve('cxh')
// 等價(jià)于
new Promise(resolve => resolve('cxh'))
如果參數(shù)是Promise實(shí)例嘶朱,那么Promise.resolve將不做任何修改、原封不動(dòng)地返回這個(gè)實(shí)例绒净。
如果參數(shù)是一個(gè)帶有then方法的實(shí)例见咒,Promise.resolve方法會(huì)將這個(gè)對(duì)象轉(zhuǎn)為Promise對(duì)象,然后就立即執(zhí)行該對(duì)象的then方法挂疆。
加入?yún)?shù)不是對(duì)象改览,則Promise.resolve方法返回一個(gè)新的Promise對(duì)象,狀態(tài)為Resolved缤言,Promise.resolve方法的參數(shù)宝当,會(huì)同時(shí)傳給回調(diào)函數(shù)。
例如
var p = Promise.resolve('cxh');
p.then(function (s){
console.log(s)
});
不帶有任何參數(shù)則胆萧,Promise.resolve方法返回一個(gè)新的Promise對(duì)象庆揩,狀態(tài)為Resolved俐东。
Promise.reject()方法也會(huì)返回一個(gè)新的 Promise 實(shí)例,該實(shí)例的狀態(tài)為rejected订晌。
done方法
因?yàn)镻romise內(nèi)部的錯(cuò)誤不會(huì)冒泡到全局虏辫,我們無(wú)法捕捉到拋出的錯(cuò)誤怎么辦?
所以提出一個(gè)done方法锈拨,在回調(diào)鏈末端砌庄,保證任何錯(cuò)誤都能拋出。
Promise.prototype.done = function (fulfilled, rejected) {
this.then(fulfilled, rejected)
.catch(function (reason) {
setTimeout(() => { throw reason }, 0);
});
};
上面的方法很簡(jiǎn)單奕枢,就是給Promise添加一個(gè)done方法娄昆,在done方法內(nèi)部,用this指向promise添加then和catch方法缝彬。
finally方法
finally方法用于指定不管Promise對(duì)象最后狀態(tài)如何萌焰,都會(huì)執(zhí)行的操作。
Promise.prototype.finally = function (callback) {
let parmise = this.constructor;
return this.then(
value => parmise.resolve(callback()).then(() => value),
reason => parmise.resolve(callback()).then(() => { throw reason })
);
};
demo實(shí)例
下面我們用promise寫(xiě)一個(gè)異步獲取數(shù)據(jù)方法:
class Axios {
constructor(){}
post(url){
console.log(1)
return this.getData(url,'POST')
}
get(url){
console.log(2)
return this.getData(url,'GET')
}
getData(url,methods){
var promise = new Promise(function(resolve, reject){
var client = new XMLHttpRequest();
client.open(methods, url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
function handler() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
});
return promise;
}
}
var axios = new Axios;
axios.get('http://localhost:3000/getData').then((value) => {
console.log(value);
});
axios.post('http://localhost:3000/postData').then((value) => {
console.log(value);
});
console.log("cxh")
node.js部分,我們使用express
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
res.render('index');
});
router.get('/getData', function(req, res, next) {
res.json({ "code": '100',"result":"get success!" });
});
router.post('/postData', function(req, res, next) {
res.json({ "code": '100',"result":"post success!" });
});
module.exports = router;
下面是我們的運(yùn)行結(jié)果:
async 函數(shù)
async 函數(shù)只不過(guò)是Generator 函數(shù)的語(yǔ)法糖谷浅。
async函數(shù)就是將 Generator 函數(shù)的星號(hào)(*)替換成async扒俯,將yield替換成await,僅此而已壳贪。
async function () {
var res1 = await axios.get('http://localhost:3000/getData');
var res2 = await axios.get('http://localhost:3000/getData?name='+res1');
console.log(res1);
console.log(res2);
};
當(dāng)執(zhí)行上面這段代碼的時(shí)候陵珍,遇到await就會(huì)跳出,繼續(xù)執(zhí)行下面的代碼违施,直到axios拿到數(shù)據(jù)互纯,再繼續(xù)向下下執(zhí)行。
await命令后面是一個(gè) Promise 對(duì)象磕蒲。如果不是留潦,會(huì)被轉(zhuǎn)成一個(gè)立即resolve的 Promise 對(duì)象。
假如辣往,我們需要請(qǐng)求一個(gè)api兔院,拿到返回的數(shù)據(jù),作為參數(shù)再去請(qǐng)求另一個(gè)api站削,拿到數(shù)據(jù)后在作為參數(shù)請(qǐng)求下一個(gè)坊萝,那么用then,該怎么寫(xiě)许起?依照上面的demo十偶。
axios.get('http://localhost:3000/getData').then((value) => {
return axios.get('http://localhost:3000/getData?name='+value.code);
}).then((res) => {
return axios.get('http://localhost:3000/getData?name='+res.code);
}).then( (value) => {
console.log(value)
}).catch(err => {});
node部分我們改成:
router.get('/getData', function(req, res, next) {
var cxh = req.query.name;
if(cxh && cxh == '100'){
res.json({ "code": '200',"result":"get2 success!" });
}else if(cxh && cxh == '200'){
res.json({ "code": '300',"result":"finish!" });
} else{
res.json({ "code": '100',"result":"get success!" });
}
});
運(yùn)行結(jié)果
要用n個(gè)then方法。
如果改稱async函數(shù)呢园细?
class Axios {
constructor(){}
post(url){
console.log(1)
return this.getData(url,'POST')
}
get(url){
console.log(2)
return this.getData(url,'GET')
}
getData(url,methods){
var promise = new Promise(function(resolve, reject){
var client = new XMLHttpRequest();
client.open(methods, url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
function handler() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
});
return promise;
}
}
var axios = new Axios;
async function getData(){
let res = await axios.get('http://localhost:3000/getData');
let val = await axios.get('http://localhost:3000/getData?name=' + res.code);
let cxh = await axios.get('http://localhost:3000/getData?name=' + val.code);
console.log(res)
console.log(val)
console.log(cxh)
}
getData();
結(jié)果:
假如惦积,上面三個(gè)await方法中有一個(gè)出現(xiàn)了reject,那么那么整個(gè)async函數(shù)都會(huì)中斷執(zhí)行猛频。
如果代買如下:
var axios = new Axios;
async function getData(){
let res = await axios.get('http://localhost:3000/getData');
let val = await axios.get('http://localhost:3000/getData?name=' + res.code);
let cxh = await axios.get('http://localhost:3000/getData?name=' + val.code);
console.log(res)
console.log(val)
console.log(cxh)
let fin = await axios.post('http://localhost:3000/postData');
}
getData();
假如上面三個(gè)方法中有一個(gè)出錯(cuò)狮崩,那么整個(gè)函數(shù)都會(huì)執(zhí)行蛛勉,也就是最后一個(gè)雖然和前面三個(gè)沒(méi)關(guān)系,依然執(zhí)行不了睦柴。那么怎么解決诽凌?
async function getData(){
try{
let res = await axios.get('http://localhost:3000/getData');
let val = await axios.get('http://localhost:3000/getData?name=' + res.code);
let cxh = await axios.get('http://localhost:3000/getData?name=' + val.code);
console.log(res)
console.log(val)
console.log(cxh)
}catch(err){
}
let fin = await axios.post('http://localhost:3000/postData');
}
這樣就ok了。
2017年8月13日