一、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.jsVue.js 练习day08files11.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.jsVue.js 练习day08files11.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
请求响应。