今天我想說說說promise對象,說到這個對象就不能不提提異步操做,那麼什麼是異步操做,什麼又是同步操做?javascript
同步操做的意思是在咱們執行某個耗時比較長的操做的時候,下面的代碼就會等待上面的代碼執行完畢而後執行。說白了代碼是順序往下執行,某些操做執行的時間順序和代碼所在的行的順序是相同的。請看下面獲取一個txt文件的例子。
同步程序示例
首先,咱們利用node先搭建一個服務器環境,默認獲取1.html靜態文件。代碼以下:css
let http = require('http'); let url = require('url'); let path = require('path'); let fs = require('fs'); http.createServer((req, res) => { var pathname = __dirname + url.parse(req.url).pathname; if (path.extname(pathname) === "") { pathname += "/"; } if (pathname.charAt(pathname.length - 1) === "/") { pathname += '1.html' } fs.exists(pathname, exists => { if (exists) { switch (path.extname(pathname)) { case ".html": res.writeHead(200, { 'Content-type': "text/html" }); break; case ".js": res.writeHead(200, { 'Content-type': "text/javascript" }); break; case ".jpg": res.writeHead(200, { 'Content-type': "image/jpeg" }); break; case ".png": res.writeHead(200, { 'Content-type': "image/png" }); case ".txt": res.writeHead(200, { 'Content-type': "text/plain" }); break; case ".css": res.writeHead(200, { 'Content-type': "text/css" }); break; default: ead res.writHead(200, { 'Content-type': "application/octet-stream" }) } fs.readFile(pathname, (err, data) => { if (err) { console.log('read file error'); } else res.end(data); }) } }) }).listen(3000); console.log('server is runing...')
咱們要從服務器用ajax獲取txt文本數據。而後從客戶端獲取數據,若是這個操做是一個同步操做。以下所示
客戶端獲取數據代碼html
function showTxt(txt) { console.log(txt); } function getDocuments(url, cb) { let xhr = new XMLHttpRequest(); xhr.open("GET", url,false); xhr.send();、 cb(xhr.responseText); } getDocuments('1.txt', showTxt); console.log("我是獲取文件以後執行的代碼");
結果以下:
等這個獲取文件這個操做執行完後,就會順序執行接下來的代碼。
這樣的操做有什麼問題呢?若是這個獲取文件的代碼耗時比較長,咱們的程序就會卡死,下面的代碼就不會執行下去,必須等獲取文件代碼執行完畢,解決問題的方法就是異步獲取文件。那麼什麼是異步操做呢?說白了就是把獲取文件的操做掛到另一個線程,先執行後面的代碼,上面的獲取文件代碼不會阻塞下面代碼的運行。這兩個操做是同時進行的。在之前,一般,若是是異步獲取數據的,那麼執行這個費時的獲取數據操做時,會指定一個回調函數,當獲取文件成功時觸發處理結果的回調函數。在這個過程當中,下面其餘的代碼會同時進行。把剛纔獲取文件的代碼改爲異步,請看下面代碼。
異步獲取數據程序示例java
function showTxt(txt) { console.log(txt); } function getDocuments(url, cb) { let xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.send(); xhr.onreadystatechange = () => { if (xhr.status === 200 && xhr.readyState == 4) { cb(xhr.responseText); } else if (xhr.readyState === 4 && xhr.status !== 200) { console.log(`${xhr.status}get error`); } }; } getDocuments('1.txt', showTxt); console.log("我是獲取文件後執行的代碼");
![圖片描述][2]
能夠看出先執行了下面的代碼而後執行了ajax獲取文件回調函數裏面的代碼,證實了下面的其餘操做的代碼會與獲取文件操做同時執行。這就是異步操做與同步操做的區別node
由上面示例能夠看到若是是一個異步操做的代碼,一般,咱們就須要爲它指定回調函數,例如上面的代碼回調函數就是onreadystatechange當它的狀態碼變成4,收到服務器響應,咱們拿到數據後再執行下步操做,先把它掛起來,讓下面的的代碼運行。可是若是因果關係變得複雜,回調事件變得不少,咱們的代碼就會變得十分像一個向右的金字塔結構不利於閱讀。
promise對象巧妙的解決了這個問題,把回調函數變成了鏈式調用,更加符合代碼書寫習慣。那麼如何使用它呢?ajax
function getTxt(url) { let p = new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.send(); xhr.onreadystatechange = () => { if (xhr.readyState !== 4) { return; } if (xhr.status === 200) { resolve(xhr.responseText); } else { reject(`${xhr.status}get error`); } } }); return p; } getTxt('1.txt').then(showTxt).catch((error) => { console.log(error); }); console.log('我是寫在異步操做以後的操做')
總結
經過控制檯打印的內容能夠看出,跟使用回調函數在結果上並無什麼異同。可是代碼結構更加清晰化,而且能夠catch到錯誤信息。
1.先new 一個promise對象,而後給它的參數傳遞一個回調函數進去,回調函數裏面有兩個參數resolve,reject都是函數。
2.resolve函數是把異步狀態變成成功,能夠把異步結果做爲它的參數傳遞到then的回調函數參數中,而then方法會在promise狀態成功時候調用;3.reject函數能夠把promise狀態變成失敗,當異步請求狀態失敗時調用,能夠傳遞出去錯誤信息,這個會觸發promise對象上的catch方法,經過promise對象的catch方法的回調函數的參數捕獲該錯誤信息。最後返回該promise對象實現鏈式調用。這就是promise對象的基本用法。promise
4.固然它還有all, race,resolve,reject等各類實例方法和靜態方法,不過上面的用法時最常常見到的,最基礎的。服務器