Promise,簡單說就是一個容器,裡面保存著某個未來才會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。
有了 Promise 對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise 對象提供統一的接口,使得控制異步操作更加容易。
Promise 也有一些缺點:
- 首先,無法取消 Promise,一旦新建它就會立即執行,無法中途取消。
- 如果不設置回調函數,Promise 內部拋出的錯誤,不會反應到外部。
- 當處於 pending 狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。
promise 的特點#
- 對象的狀態不受外界影響。Promise 對象代表一個異步操作,有三種狀態:pending(進行中)、fulfilled(已成功)和 rejected(已失敗)。
- 一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise 的對象狀態改變,要麼是成功,要麼就是失敗,只要發生這兩種情況,狀態就會凝固,稱為 resolved(已定型)。改變發生後,再添加回調函數,也是會得到這個結果。
用法#
創造 Promise 實例#
Promise 對象是一個構造函數,用來生成 Promise 實例。
const promise = new Promise(function(resolve, reject) {
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
resolve 函數的作用是,將 Promise 對象的狀態從 “未完成” 變為 “成功”(即從 pending 變為 resolved),在異步操作成功時調用,並將異步操作的結果,作為參數傳遞出去;
reject 函數的作用是,將 Promise 對象的狀態從 “未完成” 變為 “失敗”(即從 pending 變為 rejected),在異步操作失敗時調用,並將異步操作報出的錯誤,作為參數傳遞出去。
then 方法#
接著用then方法指定 resolved 狀態和 rejected 狀態的回調函數:
promise.then(function(value) {
// success
}, function(error) {
// failure
});
第一個函數是狀態對象變為 rejected 的調用,第二個是 resolved 的調用,第二個函數是可選的。
then 方法返回的是一個新的 Promise 實例(注意,不是原來那個 Promise 實例)。因此可以採用鏈式寫法,即 then 方法後面再調用另一個 then 方法。
promise.then(function(value) {
}.then(function (comments) {
console.log("resolved: ", comments);
}, function (err){
console.log("rejected: ", err);
});
catch 方法#
用於指定發生錯誤時的回調函數。
promise.then(function(value) {
// success
}, function(error) {
// failure
}).catch(function(error) {
// 處理發生的錯誤
console.log('發生錯誤!', error);
});
如果異步操作拋出錯誤,狀態就會變為 rejected,就會調用 catch () 方法指定的回調函數,處理這個錯誤。另外,then () 方法指定的回調函數,如果運行中拋出錯誤,也會被 catch () 方法捕獲。
跟傳統的 try/catch 代碼塊不同的是,如果沒有使用 catch () 方法指定錯誤處理的回調函數,Promise 對象拋出的錯誤不會傳遞到外層代碼,即不會有任何反應。
finally 方法#
用於指定不管 Promise 對象最後狀態如何,都会執行的操作。
promise.then(function(value) {
// success
}, function(error) {
// failure
}).catch(function(error) {
// 處理發生的錯誤
console.log('發生錯誤!', error);
}).finally(function(){
alert("finish");
});
finally 方法的回調函數不接受任何參數, 意味著無法知道最終的狀態是怎樣的。
Jquery、Ajax、Promise 示例#
之前學了 Jquery 和 ajax,就順手在網上隨便找了個 api 測試下:
const apiUrl = "https://suggest.taobao.com/sug?code=utf-8&q=電腦&callback=cb";
$("##bt").click(apiUrl,function(event){
const promise = new Promise(function(resolve,reject){
$.ajax({
url: event.data,
type:"get",
dataType:"jsonp",
headers: {
Accept: "application/json; charset=utf-8",
},
success: function(data) {
resolve(data);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
reject(XMLHttpRequest,textStatus,errorThrown)
},
})
})
promise.then(function(data){
console.log(data);
let x = data.result;
for(let i = 0 ; i<data.result.length;i++){
console.log(x[i][0]);
}
},function(XMLHttpRequest, textStatus, errorThrown){
alert(XMLHttpRequest);
alert(textStatus);
alert(errorThrown);
}).catch(function(error){
console.log(error);
}).finally(function(){
alert("finish");
});
});
結果:
遇到的問題#
其實在 Promise 基礎的學習上沒有什麼太大的問題,但是在 ajax 請求的時候遇上了一個跨域的問題,就是:
Access to XMLHttpRequest at ‘這裡是 api’ from origin ‘null’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
這是什麼啊……
簡單的了解了下,先說說跨域的概念吧
每個網站只能讀取同一來源的數據,這裡的同一來源指的是主機名 (域名)、協議 (http/https) 和端口號的組合。在沒明確授權的情況下,不能讀寫對方的資源,它是瀏覽器最核心也最基本的安全功能;只要有一個不一樣就跨域。
而 Ajax 的 XMLHttpRequest 受到了同源限制,只能訪問同源下的數據,所以就報這個錯。
所以怎麼還沒說怎麼解決嗷???
在搜到的文章裡面大部分都是後端配合設置一個請求權限,但是我這是野生的 api。。。我還腆著臉去要求別人做這做那哦?
直到我找到個方法,jsonp
jsonp:
- 通過 script 標籤引入某些數據,是同步模式的,用 script 標籤做跨域的時候,不建議將數據提前加載,需要按需加載;
- 當需要數據的時候創建一個 script 標籤,將需要的數據放在 src 中,通過 onload 去監聽是否請求過來,請求完畢就調用傳回來的數據(異步加載);
- jsonp 不能用 post 請求,只能是 get 請求;
所以為啥這個類型就可以跨域了???
帶 src 屬性 script、img、iframe、link 等標籤是不需要遵守同源策略的,但是通過 src 加載的資源,瀏覽器限制了 javascript 的權限,能讀不能寫
綜上所述:把 dataType 類型從 json 改成 jsonp 就可以了。