Promise 是一個對象候引,對象里存儲一個狀態(tài)韧拒,這個狀態(tài)是可以隨著內部的執(zhí)行轉化的,為以下三種狀態(tài)之一:等待態(tài)(Pending)之景、完成態(tài)(Fulfilled)、拒絕態(tài)(Rejected)膏潮。
一開始锻狗,我們先設置好等狀態(tài)從 pending 變成 fulfilled 和 rejected 的預案(當成功后我們做什么,失敗時我們做什么)。
Promise 啟動之后轻纪,當滿足成功的條件時我們讓狀態(tài)從 pending 變成 fullfilled (執(zhí)行 resolve)油额;當滿足失敗的條件,我們讓狀態(tài)從 pending 變成 rejected(執(zhí)行 reject)
Promise 方法
Promise.prototype.then / Promise.prototype.catch
如何生成一個promise對象
function A(){
return new Promise (function(x,y){
setTimeout(()=>{
x("hello")
}, 3000)
})
}
A().then((e)=>{
console.log(e) //"hello"
})
鏈式調用
function getIp() {
var promise = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getIp', true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responseText) // {"ip":"58.100.211.137"}
resolve(retJson.ip)
}
xhr.onerror = function(){
reject('獲取IP失敗')
}
xhr.send()
})
return promise
}
function getCityFromIp(ip) {
var promise = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip, true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responseText) // {"city": "hangzhou","ip": "23.45.12.34"}
resolve(retJson.city)
}
xhr.onerror = function(){
reject('獲取city失敗')
}
xhr.send()
})
return promise
}
function getWeatherFromCity(city) {
var promise = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getWeatherFromCity?city='+city, true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responseText) //{"weather": "晴天","city": "beijing"}
resolve(retJson)
}
xhr.onerror = function(){
reject('獲取天氣失敗')
}
xhr.send()
})
return promise
}
getIp().then(function(ip){
return getCityFromIp(ip)
}).then(function(city){
return getWeatherFromCity(city)
}).then(function(data){
console.log(data)
}).catch(function(e){
console.log('出現(xiàn)了錯誤', e)
})
Promise.all
function getCityFromIp(ip) {
var promise = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip, true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responseText) // {"city": "hangzhou","ip": "23.45.12.34"}
resolve(retJson)
}
xhr.onerror = function(){
reject('獲取city失敗')
}
xhr.send()
})
return promise
}
var p1 = getCityFromIp('10.10.10.1')
var p2 = getCityFromIp('10.10.10.2')
var p3 = getCityFromIp('10.10.10.3')
//Promise.all, 當所有的 Promise 對象都完成后再執(zhí)行
Promise.all([p1, p2, p3]).then(data=>{
console.log(data)
})
Promise.race
function getCityFromIp(ip) {
var promise = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip, true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responseText) // {"city": "hangzhou","ip": "23.45.12.34"}
resolve(retJson)
}
xhr.onerror = function(){
reject('獲取city失敗')
}
setTimeout(()=>{
xhr.send()
}, Math.random()*1000)
})
return promise
}
var p1 = getCityFromIp('10.10.10.1')
var p2 = getCityFromIp('10.10.10.2')
var p3 = getCityFromIp('10.10.10.3')
//Promise.all, 當所有的 Promise 對象都完成后再執(zhí)行
Promise.race([p1, p2, p3]).then(data=>{
console.log(data)
})
作用:解決回調地獄刻帚,也就是多層嵌套回調函數(shù)問題
范例:
- 實現(xiàn): 1秒鐘之后輸出 fn1, 再過疫苗輸出 fn2, 再過1秒輸出 fn3
//回調函數(shù)方法
function fn1(callback) {
setTimeout(()=>{
console.log('fn1')
callback()
}, 1000)
}
function fn2(callback) {
setTimeout(()=>{
console.log('fn2')
callback()
}, 1000)
}
function fn3() {
setTimeout(()=>{
console.log('fn3')
}, 1000)
}
fn1(function(){
fn2(function(){
fn3()
})
})
//Promise
function fn1() {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log('fn1...')
resolve()
}, 1000)
})
}
function fn2() {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log('fn2...')
resolve()
}, 1000)
})
}
function fn3() {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log('fn3...')
resolve()
}, 1000)
})
}
function onerror() {
console.log('error')
}
fn1().then(fn2).then(fn3).catch(onerror)
把如下 ajax 改造成一個返回 Promise對象的方法
改造前:jQuery 的 success / error 形式回調
function ajax(opts){
var url = opts.url
var type = opts.type || 'GET'
var dataType = opts.dataType || 'json'
var onsuccess = opts.onsuccess || function(){}
var onerror = opts.onerror || function(){}
var data = opts.data || {}
var dataStr = []
for(var key in data){
dataStr.push(key + '=' + data[key])
}
dataStr = dataStr.join('&')
if(type === 'GET'){
url += '?' + dataStr
}
var xhr = new XMLHttpRequest()
xhr.open(type, url, true)
xhr.onload = function(){
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
//成功了
if(dataType === 'json'){
onsuccess( JSON.parse(xhr.responseText))
}else{
onsuccess( xhr.responseText)
}
} else {
onerror()
}
}
xhr.onerror = onerror
if(type === 'POST'){
xhr.send(dataStr)
}else{
xhr.send()
}
}
ajax({
url: 'http://api.jirengu.com/weather.php',
data: {
city: '北京'
},
onsuccess: function(ret){
console.log(ret)
},
onerror: function(){
console.log('服務器異常')
}
})
改造后:Promise回調
function ajax(opts) {
var url = opts.url
var type = opts.type || 'GET'
var dataType = opts.dataType || 'json'
var data = opts.data || {}
var dataStr = []
for (var key in data) {
dataStr.push(key + '=' + data[key])
}
dataStr = dataStr.join('&')
if (type === 'GET') {
url += '?' + dataStr
}
var promise = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest()
xhr.open(type, url, true)
xhr.onload = function () {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
//成功了
if (dataType === 'json') {
var ret = ( JSON.parse(xhr.responseText))
resolve(ret)
} else {
var ret = ( xhr.responseText)
resolve(ret)
}
} else {
reject()
}
}
if (type === 'POST') {
xhr.send(dataStr)
} else {
xhr.send()
}
})
return promise;
}
ajax({
url: 'http://api.jirengu.com/weather.php',
data: {
city: '北京'
}
}).then(function(ret){
console.log(ret)
}).catch(function(){
console.log('服務器異常')
})
簡化版
function ajax(){
return new Promise((resolve, reject)=>{
做事
如果成功就調用 resolve
如果失敗就調用 reject
})
}
var promise = ajax()
promise.then(successFn, errorFn)
在 Node.js中的應用
回調函數(shù)的第一個參數(shù)潦嘶,必須是錯誤對象err(如果沒有錯誤,該參數(shù)就是null);原因是執(zhí)行分成兩段崇众,在這兩段之間拋出的錯誤掂僵,程序無法捕捉,只能當作參數(shù)顷歌,傳入第二段
fs.readFile(fileA, 'utf-8', function (err, data) {
fs.readFile(fileB, 'utf-8', function (err, data) {
if (err) throw err;
console.log(data);
});
});
var readFile = require('fs-readfile-promise');
readFile(fileA)
.then(function (data) {
console.log(data.toString());
})
.then(function () {
return readFile(fileB);
})
.then(function (data) {
console.log(data.toString());
})
.catch(function (err) {
console.log(err);
});