Vue.js 之 Promise 物件(七)
一、Promise 概念
Promise
是一個建構函式,new Promise()
可以得到一個 Promise
例項物件,它是一個非同步操作,可以用來執行一些非同步操作(非同步操作不能直接 return 接收執行結果,只能通過回撥來接收)。
回撥函式
resolve()
:成功之後呼叫的回撥函式reject()
:執行失敗呼叫的回撥函式
例項物件/方法
Prototype
屬性有一個 .then()
方法,它可以預先為 Promise
非同步操作指定 成功 resolve()
和失敗 reject()
的回撥,Promise
例項物件可直接呼叫 .then()
方法。
注意:可以在瀏覽器除錯介面列印輸出
Promise
物件的內建方法,使用方法:console.dir(Promise)
二、形式上的非同步操作和具體的非同步操作
形式上的非同步操作(只是形式上的,並沒有其他任何非同步操作):
var promise = new Promise()
具體的非同步操作:
var promise = new Promise(function(){
// function 內部就是具體的非同步操作
})
三、快速上手
建立一個 Promise
物件,用於讀取檔案內容,新建 read_file.js
:
const fs = require('fs') // new 一個 Promise() 物件,內部非同步操作:讀寫檔案 var promise = new Promise(function () { fs.readFile('./files/1.txt', 'utf-8', (err, resp) => { if (err) throw err // if (err) throw err // 若讀寫檔案有錯,則 throw 掉,不執行這段 console.log(resp) }) })
命令列執行:node read_file.js
,執行 read_file.js
檔案,發現這個 Promise
例項會被立即執行,這是因為 每當 new 一個 Promise 例項的時候,就會立即 執行這個 非同步操作中的程式碼。
要想不立即執行,而是需要的時候再呼叫,可以將其封裝到函式中:
function readFile(file_path) { var promise = new Promise(function () { fs.readFile(file_path, 'utf-8', (err, resp) => { if (err) throw err // if (err) throw err // 若讀寫檔案有錯,則 throw 掉,不執行這段 console.log(resp) }) }) } readFile('./files/1.txt')
四、通過 then 指定回撥
非同步操作不能直接 return
獲取執行結果,而是需要通過回撥函式獲取,Promise
中可以通過 .then()
來指定回撥。
const fs = require('fs')
function getFile(file_path) {
var promise = new Promise(function (resolve, reject) {
fs.readFile(file_path, 'utf-8', (err, resp) => {
if (err) return reject(err) // 失敗的回撥
// 成功的回撥
resolve(resp)
})
})
return promise
}
var p = getFile('./files/1.txt')
// 預先指定回撥
p.then(function(resp) {
// 執行成功
console.log('執行成功:', resp)
}, function(err) {
// 執行失敗
console.log('執行失敗:', err)
})
這裡將 promise
例項物件返回,再用一個變數 p
接收,通過 p
呼叫 then()
方法從而預先指定回撥;讀取檔案這個非同步操作不會立即執行,而是等 then()
指定了回撥後才執行。
也可以省略接收變數,直接呼叫 then()
:
function getFile(file_path) {
return new Promise(function (resolve, reject) {
fs.readFile(file_path, 'utf-8', (err, resp) => {
if (err) return reject(err) // 失敗的回撥
// 成功的回撥
resolve(resp)
})
})
}
getFile('./files/1.txt')
.then(function(resp) {
// 執行成功
console.log('執行成功:', resp)
}, function(err) {
// 執行失敗
console.log('執行失敗:', err)
})
五、promise 解決回撥地獄問題
以此讀取三個檔案,出現的回撥地獄問題:
const fs = require('fs')
function getFile(file_path) {
return new Promise(function (resolve, reject) {
fs.readFile(file_path, 'utf-8', (err, resp) => {
if (err) return reject(err) // 失敗的回撥
// 成功的回撥
resolve(resp)
})
})
}
getFile('./files/1.txt')
.then(function(resp1) {
// 執行成功
console.log('1.txt 執行成功:', resp1)
getFile('./files/2.txt')
.then(function(resp2) {
console.log('2.txt 執行成功:', resp2)
getFile('./files/3.txt')
.then(function(resp3) {
console.log('3.txt 執行成功:', resp3)
})
})
})
promise 解決回撥地獄
getFile('./files/1.txt')
.then(function(resp1) {
console.log('1.txt 執行成功:', resp1)
return getFile('./files/2.txt')
})
.then(function(resp2) {
console.log('2.txt 執行成功:', resp2)
return getFile('./files/3.txt')
})
.then(function(resp3) {
console.log('3.txt 執行成功:', resp3)
})
promise
採用的是鏈式呼叫,而不是巢狀呼叫。
同時指定異常回調:
getFile('./files/1.txt')
.then(function(resp1) {
console.log('1.txt 執行成功:', resp1)
return getFile('./files/2.txt')
}, function(err1) {
console.log('讀取 1.txt 出錯:', err1)
})
.then(function(resp2) {
console.log('2.txt 執行成功:', resp2)
return getFile('./files/3.txt')
})
.then(function(resp3) {
console.log('3.txt 執行成功:', resp3)
})
注意:異常回調通常可省略!
六、捕獲 promise 中的異常
當有多個 promise
"巢狀使用" 出現異常時,通常會有以下兩種處理情形:
- 前面的
promise
出現異常,不影響後續的promise
執行:通常給每個promise
指定異常回調 - 前面的
promise
一旦出現異常,直接捕獲異常,後續的promise
不執行:通常使用catch()
捕獲異常
情形一
// 不存在的檔案
getFile('./files/11.txt')
.then(function(resp1) {
console.log('1.txt 執行成功:', resp1)
return getFile('./files/2.txt')
}, function(err1) {
console.log('讀取 1.txt 出錯:', err1)
})
.then(function(resp2) {
console.log('2.txt 執行成功:', resp2)
return getFile('./files/3.txt')
}, function(err2) {
console.log('讀取 2.txt 出錯:', err2)
})
.then(function(resp3) {
console.log('3.txt 執行成功:', resp3)
}, function(err3) {
console.log('讀取 3.txt 出錯:', err3)
})
執行 node read_file.js
,執行結果:
> node "03. promise 解決回撥地獄問題.js"
讀取 1.txt 出錯: [Error: ENOENT: no such file or directory, open 'F:\200-原始碼\342-Vue.js\黑馬205集-Vue.js\Vue.js 練習\day08\files\11.txt'] {
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'F:\\200-原始碼\\342-Vue.js\\黑馬205集-Vue.js\\Vue.js 練習\\day08\\files\\11.txt'
}
2.txt 執行成功: undefined
3.txt 執行成功: 333
情形二
getFile('./files/11.txt')
.then(function(resp1) {
console.log('1.txt 執行成功:', resp1)
return getFile('./files/2.txt')
})
.then(function(resp2) {
console.log('2.txt 執行成功:', resp2)
return getFile('./files/3.txt')
})
.then(function(resp3) {
console.log('3.txt 執行成功:', resp3)
})
.catch(function (err) {
console.log('執行異常::', err.message)
})
console.log('OK')
執行結果:
OK
> node "03. promise 解決回撥地獄問題.js"
執行異常:: ENOENT: no such file or directory, open 'F:\200-原始碼\342-Vue.js\黑馬205集-Vue.js\Vue.js 練習\day08\files\11.txt'
注意:當
promise
中發生異常時,不會影響主程式後續的程式執行,上述程式碼中,會先執行console.log("OK")
七、Ajax 中使用 promise
$(function () {
$('#btn').on('click', function () {
$.ajax({
url: './data.json',
type: 'get',
dataType: 'json'
})
.then(function (data) {
console.log(data)
})
})
});
去掉原有的 success(resp)
,而是使用 then()
來處理 ajax
請求響應。