摘要:這時,第二個方法指定的回調函數,就會等待這個新的對象狀態發生變化。
1.概述
javascript是單線程語言(單線程/多線程、阻塞/非阻塞、同步、異步)參考此文章,所有的任務都是按順序執行的,但是對于很耗時的操作采用異步的方式(前一個任務結束的時候,不是執行下一個任務,而是執行回調函數,后一個任務則是不等前一個任務結束就執行)參考此文章,js中異步編程的四種方法:回調函數、事件監聽、發布/訂閱、Promise對象,這里討論promise的用法,promise是es6增加的內容,使得在異步編程中不用過多的嵌套回調函數,讓異步操作變得更加簡單
2.Promise介紹 2.1 Promise構造函數Promise構造函數接收一個函數作為參數,此函數又有兩個參數分別為resolve和reject,這兩個參數也是函數
const promise = new Promise(function(resolve, reject) { // 異步操作的代碼 if(success) { return resolve(data); // data為異步操作成功返回的數據 } else { return reject(error); //data為失敗時返回的數據 } })2.2 resolve和reject
promise對象類似于一個容器(也可以說是一個狀態機),里面包含著異步操作,異步操作會有兩種結果:成功或失敗。當異步操作成功就會將pending(執行中狀態)轉為fulfilled(成功狀態)同時觸發resolve函數,用來將操作成功后得到的結果傳遞出去;當異步操作失敗就會將pending(執行中狀態)轉為reject(拒絕狀態)同時觸發reject函數,用來將操作失敗后報出的錯誤傳遞出去
const promise = new Promise(function(resolve, reject) { // 異步操作的代碼 if(success) { return resolve(data); // data為異步操作成功返回的數據 } else { return reject(error); //data為失敗時返回的數據 } })
我們來看下面代碼:
function fn1() { return new Promise(function(resolve, reject){ setTimeout(() => { console.log("fn1") },1000) }) } function fn2() { return new Promise(function(resolve, reject){ setTimeout(() => { console.log("fn2") },2000) }) } fn1().then(fn2)
輸出為:
fn1
fn2函數不執行,這個時候我們需要調用resolve函數以便執行then()方法中的回調函數fn2
function fn1() { return new Promise(function(resolve, reject){ setTimeout(() => { console.log("fn1") resolve("fn1") },1000) }) }
輸出為:
fn1
fn2
如果我們在fn1中調用reject函數時會出現什么情況呢?
function fn1() { return new Promise(function(resolve, reject){ setTimeout(() => { console.log("fn1") // resolve("fn1") reject("錯誤"+"fn1") },1000) }) } fn1().then(fn2).then((data) => { console.log(data) })
輸出為:
提示錯誤未捕獲,所以需要在then()方法中添加第二個回到函數來處理出錯信息
fn1().then(fn2).then((data) => { console.log(data) }, (err) => { console.log(err) })
輸出為:
當promise實例生成以后,后面跟then方法,其的第一個回調函數來處理resolve函數傳遞的數據,第二個回調函數來處理reject函數傳遞的數據,以上的流程用代碼展示如下
promise .then(function(data){ //拿到返回的數據之后做一些處理 console.log(data) }, function(error) { //返回失敗時做一些處理 console.log(error) })2.3.1 then()的返回值
then()方式是Promise實例的方法,此then方法定義在原型對象(Promise.prototype)上,then()方法的返回值是一個新的Promise實例(不是原來那個Promise,原來那個Promise已經承諾過)所以我們可以采用鏈式的寫法
var p1 = new Promise( (resolve, reject) => { setTimeout(() => resolve("p1"), 10); }); p1.then( ret => { console.log(ret); return "then1"; }).then( ret => { console.log(ret); return "then2"; }).then( ret => { console.log(ret); });
執行順序:
p1>then1>then2
從第二個then()方法開始,它們的resolve中的參數就是前一個then()中的resolve的return語句的返回值
采用鏈式的then,可以指定一組按照次序調用的回調函數。這時,前一個回調函數,有可能返回的還是一個Promise對象(即有異步操作),這時后一個回調函數,就會等待該Promise對象的狀態發生變化,才會被調用
getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL);}).then(function funcA(comments) { console.log("resolved: ", comments);}, function funcB(err){ console.log("rejected: ", err);})
上面代碼中,第一個then方法指定的回調函數,返回的是另一個Promise對象。這時,第二個then方法指定的回調函數,就會等待這個新的Promise對象狀態發生變化。如果變為resolved,就調用funcA,如果狀態變為rejected,就調用funcB。
如果采用箭頭函數,上面的代碼可以寫得更簡潔
getJSON("/post/1.json").then( post => getJSON(post.commentURL)).then( comments => console.log("resolved: ", comments), err => console.log("rejected: ", err));
promise鏈式寫法如下:
function fn1() { return new Promise(function(resolve, reject){ setTimeout(() => { console.log("fn1") resolve("fn1") },1000) }) } function fn2() { return new Promise(function(resolve, reject){ setTimeout(() => { console.log("fn2") resolve("fn2") },2000) }) } function fn3() { return new Promise(function(resolve, reject){ setTimeout(() => { console.log("fn3") resolve("fn3") },3000) }) } function fn4() { return new Promise(function(resolve, reject){ setTimeout(() => { console.log("fn4") resolve("fn4") },4000) }) } fn1().then(fn2).then(fn3).then((data) => { console.log(data) }, (err) => { console.log(err) })
依次輸出為:fn1>fn2>fn3
同時我們還可以更改執行的先后順序
function fn1() { return new Promise(function(resolve, reject){ setTimeout(() => { console.log("fn1") reject(false) },1000) }) } function fn2() { return new Promise(function(resolve, reject){ setTimeout(() => { console.log("fn2") resolve("fn2") },2000) }) } function fn3() { return new Promise(function(resolve, reject){ setTimeout(() => { console.log("fn3") resolve("fn3") },3000) }) } function fn4() { return new Promise(function(resolve, reject){ setTimeout(() => { console.log("fn4") resolve("fn4") },4000) }) } fn1().then(fn2).then(fn3).then((data) => { console.log(data) }, (err) => { if(err == false){ fn3().then(fn4) } })
輸出為:fn1>fn3>fn4
2.4 catch()方法我們在開發時傾向于用catch()方法來處理異常,而不是在then()方法里寫入第二個回調函數,這種寫法類似于.then(null, rejection)
promise .then(function(data){ //拿到返回的數據之后做一些處理 console.log(data) }) .catch(function(error) { //返回失敗時做一些處理 console.log(error) }
為什么要采用這么catch()方法呢?我們來看一個例子:
const promise = new Promise((resolve,reject) => { console.log(1) resolve("成功") }) promise .then((data) => { console.log(data) console.log(a) }, (err) => { console.log(err) })
輸出為:
1
成功
但是沒有捕捉到回調函數里a未定義
這個時候我們來該寫以上代碼
1
成功
ReferenceError: a is not defined
Promise對象的Error對象具有冒泡性質,會一直向后傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch語句捕獲
var p = new Promise( (resolve, reject) => { setTimeout(() => resolve("p1"), 10); }); p.then( ret => { console.log(ret); throw new Error("then1"); return "then1"; }).then( ret => { console.log(ret); throw new Error("then2"); return "then2"; }).catch( err => { // 可以捕抓到前面的出現的錯誤。 console.log(err.toString()); });
輸出為:
p1
Error: then1
既然catch()是.then(null, rejection)的別名,那么catch()就會返回一個Promise對象,因此在后面還可以接著調用then方法
var p = new Promise((resolve, reject) => { resolve(x + 2); }); p.then( () => { console.log("nothing"); }).catch( err => { console.log(err.toString()); return "catch"; }).then( ret => { console.log(ret); });
輸出為:
ReferenceError: x is not defined
catch
當出錯時,catch會先處理之前的錯誤,然后通過return語句,將值繼續傳遞給后一個then方法中,如果沒有報錯,則跳過catch,示例如下:
var p = new Promise((resolve, reject) => { resolve("p"); }); p.then( ret => { console.log(ret); return "then1"; }).catch( err => { console.log(err.toString()); return "catch"; }).then( ret => { console.log(ret); });3. promise用法解析 3.1 用Promise實現ajax操作
const getJSON = function(url) { const promise = new Promise(function(resolve, reject){ const handler = function() { if (this.readyState !== 4) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; const client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); }); return promise; }; getJSON("/posts.json").then(function(json) { console.log("Contents: " + json); }, function(error) { console.error("出錯了", error); });3.2 promise和ajax方法結合
var http = { get: function(url) { var promise = new Promise(function(resolve, reject) { $.ajax({ url: url, method: "get", success: function(data) { resolve(data); }, error: function(xhr, statusText) { reject(statusText); } }); }); return promise; } }; http.get("data.php").then(function(data) { document.write(data); }, function(err) { document.write(err); });3.3 用promise實現異步加載圖片的例子
function loadImageAsync(url) { return new Promise(function(resolve, reject) { const image = new Image(); image.onload = function() { resolve(image); }; image.onerror = function() { reject(new Error("Could not load image at " + url)); }; image.src = url; }); } var loadImage1 = loadImageAsync(url); loadImage1.then(function success() { console.log("success"); }, function fail() { console.log("fail"); });3.4 promise和axios方法結合
function fetch(url, params) { return new Promise((resolve, reject) => { axios.post(url, params) .then(response => { resolve(response.data); }, error => { reject(error); }) .catch(error => { reject(error) }) }) } function lineStatus(params) { return fetch("/ProductionLine/GetStatus", params) } function statisticsData(params) { return fetch("/ProductionLine/GetWeightStatistics", params) } lineStatus(this.lineId) .then((result) => { this.deviceStatus.run = result.TotleMoveMotor this.deviceStatus.stop = result.TotleStopMotor this.deviceStatus.lost = result.TotleLoseMotor this.deviceStatus.alarm = result.TotleAlarmMotor this.ProductionStatus = result.ProductionStatus console.log(result) }) .catch((error) => { console.log("瓦特了...(;′⌒`)") }) statisticsData(this.lineId) .then((result) => { this.outputData.totalOutput = result.MainConveyorModel.OutPut this.outputData.instantWeight = result.MainConveyorModel.InstantWeight this.outputData.runningTime = result.MainConveyorModel.RunningTime this.outputData.motorLoad = result.MainConveyorModel.MotorLoad // console.log(result) }) .catch((error) => { console.log("瓦特了...(;′⌒`)") })
以上是Promise在開發中常見的用法,參考了以下幾篇文章,特此感謝作者
Promise 對象[阮一峰]
[es6系列]學習Promise
阮一峰的ES6---Promise對象
對Promise中的resolve,reject,catch的理解
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107121.html
摘要:概述是異步編程的一種解決方案,很好的解決了傳統異步編程中的回調地獄問題。語法中闡述,有三種狀態,分別是進行中已完成已失敗。方法使用要使用方法我們首先要創建實例,也就是說我們要一個對象。 1. Promise概述 promise是異步編程的一種解決方案,很好的解決了傳統異步編程中的回調地獄問題。同時我們可以把promise可以理解為一個容器,這個容器里面存放著一些未來才會結束的事件(通常...
摘要:有了對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,對象提供統一的接口,使得控制異步操作更加容易。它的作用是為實例添加狀態改變時的回調函數。這時,第二個方法指定的回調函數,就會等待這個新的對象狀態發生變化。 Promise 是異步編程的一種解決方案,比傳統的解決方案(回調函數和事件)更合理和更強大。它由社區最早出和實現,ES6 將其寫進了語言標準,統一了...
摘要:在服務工作線程中,延長事件的壽命從而阻止瀏覽器在事件中的異步操作完成之前終止服務工作線程。在事件中,它延遲將視為已激活的,直到傳遞的被成功地。這主要用于確保任何功能事件不會被分派到對象,直到它升級數據庫模式并刪除過期的緩存條目。 看了很多介紹Service Worker的,看得都挺模糊的,所以決定自己寫一篇文件整理一下思路。 一、Service Worker API 名詞區分 1、Se...
摘要:就是每次傳入的函數最后是的任務之后,開始執行,可以看到此時會批量執行中的函數,而且還給這些中回調函數放入了一個這個很顯眼的函數之中,表示這些回調函數是在微任務中執行的。下一模塊會對此微任務中的插隊行為進行詳解。 有關Eventloop+Promise的面試題大約分以下幾個版本——得心應手版、游刃有余版、爐火純青版、登峰造極版和究極變態版。假設小伙伴們戰到最后一題,以后遇到此類問題,都是...
閱讀 3027·2021-11-02 14:40
閱讀 844·2019-08-30 15:53
閱讀 1265·2019-08-30 15:53
閱讀 3259·2019-08-30 13:53
閱讀 3305·2019-08-29 12:50
閱讀 1132·2019-08-26 13:49
閱讀 1863·2019-08-26 12:20
閱讀 3660·2019-08-26 11:33