一、什么是Promise?
Promise是 ES6 引入的进行异步编程的新的解决方案。 备注:旧的是单纯的回调函数
从语法上来说:它就是一个构造函数,可以封装异步的任务,并且对结果进行处理。 从功能上来说:Promise对象用来封装一个异步操作并可以获取其成功/失败 的结果值。
Promise最大的好处在于可以解决回调地狱的问题,并且它在指定回调与错误处理这块要更加的灵活与方便,而且Promise在现代项目当中,无论是Web还是App的项目当中都应用的十分广泛,无论是前端还是后端都可以看到它的身影,同时它也是现在面试的高频题目。
二、Promise 初体验
2.1 在nodejs环境下读取文件内容
需求:读取当前目录下的 file 文件夹下的 content.txt 的内容并输出。
// 引入fs模块const fs = require('fs')// 回调函数形式// fs.readFile('./file/content.txt', (err, data) => {// // 如果错误,则抛出错误// if(err) throw err// console.log(data.toString());// })// Promise新式let p = new Promise((resolve, reject) => { fs.readFile('./file/content.txt', (err, data) => { if (err) reject(err); resolve(data); })})p.then(value =>{ console.log(value.toString());},reason =>{ console.log(reason);})
output:
Hello Promise,This is Content1
2.2 封装 AJAX
需求:使用Promise封装AJAX并读取接口数据并输出 接口地址: api.apiopen.top/getJoke
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <!-- 引入bootstrap 的样式 --> <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.css"></head><body> <div class="container"> <h2 class="page-header">Promise 封装 AJAX操作</h2> <button class="btn btn-primary" id="btn">点击发送AJAX</button> </div> <script> const btn = document.querySelector('#btn') btn.onclick = function () { const p = new Promise((resolve, reject) => { xhr = new XMLHttpRequest() xhr.open('GET', 'https://api.apiopen.top/getJoke') xhr.send() xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response) } else { reject(xhr.status) } } } }) p.then(value => { console.log(value); }, reason => { console.warn(reason); }) } </script></body></html>
三、怎么使用 Promise?
在了解怎么使用 Promise之前,我们先来看看 Promise实例对象的两个属性: 「PromiseState」和「PromiseResult」
3.1 Promise 的状态
每一个实例对象都拥有一个属性叫做 「PromiseState」,这个属性有三个值,分别是:
-
pending 「未决定的」
-
resolved / fullfilled 「成功」
-
rejected 「失败」
状态的改变
-
pending变为 resolved
-
pending变为 rejected
说明:只有这2种,且一个 promise对象只能改变一次,改变后不可修改 无论变为成功还是失败,都会有一个结果数据 成功的结果数据一般称为 value,失败的结果数据一般称为 reason
3.2 Promise 的结果
每一个实例对象都拥有一个属性叫做 「PromiseResult」, 它用于保存实例对象(异步任务) 「成功或失败」 的结果。
只有 resolve和reject 可以修改该属性的值。
四、 Promise 的 API
4.1 resolve 方法
如果传入的参数为非 Promise类型的对象,则返回的结果为成功 promise对象 如果传入的参数为 Promise对象,则参数的结果决定了 resolve的结果
const p1 = Promise.resolve(520)console.log(p1); // PromiseState => fulfilledconst p2 = Promise.resolve(new Promise((resolve, reject) => { reject('111')}))// 此处内部有一个失败的回调,但是没有处理.所以会产生报错console.log(p2); // PromiseState => rejected 报错:Uncaught (in promise) 111p2.catch(reason => { console.log(reason); // 111})
4.2 reject 方法
永远返回一个 rejected「失败」的 Promise 对象。
// const p = Promise.reject(521)// 注意此处会产生一个报错,原因是内部有一个失败的Promise但是没有对应的回调来处理它// console.log(p); // PromiseState rejectedconst p2 = Promise.reject(new Promise((resolve, reject) => { resolve('123')}))console.log(p2); // PromiseState rejectedp2.then(value =>{ console.log(value);},reason =>{ console.log(reason); // 失败的结果 是一个 Promise对象,这个Promise对象的状态为成功 })
4.3 all 方法
语法:
Promise.all(Array) // Array 一组Promise对象1
说明:返回一个新的 Promise,只有所有的 Promise都成功才成功,只要有一个失败了就 直接失败
第一种情况,全部都是 resolved:
const p1 = new Promise((resolve, reject) => { resolve('ok')})const p2 = Promise.resolve('success')const p3 = Promise.resolve('Oh Yeah')const result = Promise.all([p1, p2, p3])console.log('result: ', result); // PromiseState "fulfilled"// PromiseResult ['ok','success','Oh Yeah']
第二种情况,有一个是 rejected:
const p1 = new Promise((resolve, reject) => { resolve('ok')}) const p2 = Promise.reject('error info')const p3 = Promise.resolve('Oh Yeah')const result = Promise.all([p1, p2, p3])console.log('result: ', result);// PromiseState "rejected"// PromiseResult "error info"
4.4 race方法
race函数返回一个 Promise,它可以是完成( resolved),也可以是失败(rejected),这要取决于第一个完成的是哪个。
第一种情况,先完成的是 「P1 => resolved」 :
const p1 = new Promise((resolve, reject) => { resolve('ok')})const p2 = Promise.reject('error')const p3 = Promise.resolve('Oh Yeah')const result = Promise.race([p1, p2, p3])console.log(result); // PromiseState:"fulfilled" PromiseResult:"ok"
第二种情况,先完成的是 「P2 => rejected」:
//第一钟测试方式://注意:这里改变的是race方法参数的位置const p1 = new Promise((resolve, reject) => { resolve('ok')})const p2 = Promise.reject('error info')const p3 = Promise.resolve('Oh Yeah')const result = Promise.race([p2, p1, p3])console.log(result); // PromiseState:"rejected" PromiseResult:"error info"//第二钟测试方式:const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('ok') }, 1000);})const p2 = Promise.reject('error info')const p3 = Promise.resolve('Oh Yeah')const result = Promise.race([p1, p2, p3])console.log(result); // PromiseState:"rejected" PromiseResult:"error info"
4.5 实例对象的 then方法
then 方法拥有返回值,返回值是一个 Promise 对象。状态是由回调函数的执行结果来决定.
-
返回结果是非 Primose 类型,状态为成功,返回值为then方法中的返回值(下方例子中的 123即为返回值),方法中如果没有return 则为 undefined
-
如果返回值是一个 Promise 对象, 那么内部 Promise 对象返回的状态和值决定外部 then 方法的状态和值
-
如果抛出错误,则then方法状态为 rejected,值为抛出的值
提示: then方法的返回值与 async 修饰的函数的返回值一模一样
const p = new Promise((resolve, reject) => { resolve('ok')})const result = p.then(value => { // 1.返回普通数据 状态为成功,没有return状态也为成功,值为undefined // return 123; // 2.抛出错误,状态为失败,值为抛出的值 // throw '出错啦' // 3.返回一个 Promise ,内部的Promise决定外部then方法的状态及返回值 return new Promise((resolve, reject) => { // resolve() // reject() throw '出错啦!'; })}, err => { console.error(err);})console.log(result); // PromiseState: "rejected" PromiseResult: "出错啦!"
特殊情况:
then方法期望的参数是一个函数,如果不是函数则会发生 Promise穿透(值穿透),状态为上一个Promise的状态,值为上一个 Promise的值。
// 特殊情况:const p = new Promise((resolve, reject) => { resolve('ok') // reject('error')})const result = p.then(console.log(123))console.log(result); // PromiseState: "fulfilled" PromiseResult: "ok"1234567
稍微加点难度:
Promise.resolve('foo') .then(Promise.resolve('bar')) .then(function(result){ console.log(result) })
当然,输出的结果为foo。问其原因,答案如题——Promise值穿透 解释:.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。
再来一道:
Promise.resolve(1) .then(function(){return 2}) .then(Promise.resolve(3)) .then(console.log)
output: 2 解释:Promise.resolve(3) 不是函数,发生了值穿透
继续来:
Promise.resolve(1) .then(function(){return 2}) .then(function(){return Promise.resolve(3)}) .then(console.log)
output:3
五、async 和 await
5.1 async
使用 async 关键字 修饰的函数 返回值永远为 Promise 对象,这个Prmose对象的状态与then方法返回值的状态都是应用的同一种规则。
一个最简单的例子:
async function main(){ }let result = main()console.log(result); // "fulfilled" "undefined"
5.2 await
await 右侧的表达式一般为 promise对象,但也可以是其它的
-
右侧没有Promise的情况: 直接将此值作为 await的返回值
-
右侧为成功的 Promise: 返回的是 promise成功的值
-
右侧为失败的 Promise: 抛出异常,需要使用 try catch 捕获处理
注意: await 必须写在 async函数中,但 async函数中可以没有 await
async function main() { // 1.右侧没有Promise的情况: 直接将此值作为 await的返回值 let result = await 'str' console.log(result); // result => str info => fulfilled str return result // 2.右侧为成功的 Promise: 返回的是 promise成功的值 // let p1 = new Promise((resolve, reject) => { // resolve('ok') // }) // let result1 = await p1 // console.log(result1); // result => ok info => fulfilled ok // return result1 // 3.右侧为失败的 Promise: 抛出异常,需要使用 try catch 捕获处理 // let p2 = new Promise((resolve, reject) => { // reject('error') // }) // try { // let result2 = await p2 // } catch (e) { // console.log(e); // e => error info => fulfilled error // return e // }}const info = main()console.log(info);
六、async与await 结合发送 AJAX
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <!-- 引入bootstrap 的样式 --> <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.css"></head><body> <div class="container"> <h2 class="page-header">async与await封装AJAX操作</h2> <button class="btn btn-primary" id="btn">点击发送AJAX</button> </div> <script> function sendAJAX(url) { return new Promise((resolve, reject) => { xhr = new XMLHttpRequest() xhr.responseType = 'json' xhr.open('GET', url) xhr.send() xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response) } else { reject(xhr.status) } } } }) } const btn = document.querySelector('#btn') btn.addEventListener('click', async function () { let result = await sendAJAX('https://api.apiopen.top/getJoke') console.log(result); }) </script></body></html>
一、什么是Promise?
Promise是 ES6 引入的进行异步编程的新的解决方案。 备注:旧的是单纯的回调函数
从语法上来说:它就是一个构造函数,可以封装异步的任务,并且对结果进行处理。 从功能上来说:Promise对象用来封装一个异步操作并可以获取其成功/失败 的结果值。
Promise最大的好处在于可以解决回调地狱的问题,并且它在指定回调与错误处理这块要更加的灵活与方便,而且Promise在现代项目当中,无论是Web还是App的项目当中都应用的十分广泛,无论是前端还是后端都可以看到它的身影,同时它也是现在面试的高频题目。
二、Promise 初体验
2.1 在nodejs环境下读取文件内容
需求:读取当前目录下的 file 文件夹下的 content.txt 的内容并输出。
// 引入fs模块const fs = require('fs')// 回调函数形式// fs.readFile('./file/content.txt', (err, data) => {// // 如果错误,则抛出错误// if(err) throw err// console.log(data.toString());// })// Promise新式let p = new Promise((resolve, reject) => { fs.readFile('./file/content.txt', (err, data) => { if (err) reject(err); resolve(data); })})p.then(value =>{ console.log(value.toString());},reason =>{ console.log(reason);})
output:
Hello Promise,This is Content1
2.2 封装 AJAX
需求:使用Promise封装AJAX并读取接口数据并输出 接口地址: api.apiopen.top/getJoke
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <!-- 引入bootstrap 的样式 --> <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.css"></head><body> <div class="container"> <h2 class="page-header">Promise 封装 AJAX操作</h2> <button class="btn btn-primary" id="btn">点击发送AJAX</button> </div> <script> const btn = document.querySelector('#btn') btn.onclick = function () { const p = new Promise((resolve, reject) => { xhr = new XMLHttpRequest() xhr.open('GET', 'https://api.apiopen.top/getJoke') xhr.send() xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response) } else { reject(xhr.status) } } } }) p.then(value => { console.log(value); }, reason => { console.warn(reason); }) } </script></body></html>
三、怎么使用 Promise?
在了解怎么使用 Promise之前,我们先来看看 Promise实例对象的两个属性: 「PromiseState」和「PromiseResult」
3.1 Promise 的状态
每一个实例对象都拥有一个属性叫做 「PromiseState」,这个属性有三个值,分别是:
-
pending 「未决定的」
-
resolved / fullfilled 「成功」
-
rejected 「失败」
状态的改变
-
pending变为 resolved
-
pending变为 rejected
说明:只有这2种,且一个 promise对象只能改变一次,改变后不可修改 无论变为成功还是失败,都会有一个结果数据 成功的结果数据一般称为 value,失败的结果数据一般称为 reason
3.2 Promise 的结果
每一个实例对象都拥有一个属性叫做 「PromiseResult」, 它用于保存实例对象(异步任务) 「成功或失败」 的结果。
只有 resolve和reject 可以修改该属性的值。
四、 Promise 的 API
4.1 resolve 方法
如果传入的参数为非 Promise类型的对象,则返回的结果为成功 promise对象 如果传入的参数为 Promise对象,则参数的结果决定了 resolve的结果
const p1 = Promise.resolve(520)console.log(p1); // PromiseState => fulfilledconst p2 = Promise.resolve(new Promise((resolve, reject) => { reject('111')}))// 此处内部有一个失败的回调,但是没有处理.所以会产生报错console.log(p2); // PromiseState => rejected 报错:Uncaught (in promise) 111p2.catch(reason => { console.log(reason); // 111})
4.2 reject 方法
永远返回一个 rejected「失败」的 Promise 对象。
// const p = Promise.reject(521)// 注意此处会产生一个报错,原因是内部有一个失败的Promise但是没有对应的回调来处理它// console.log(p); // PromiseState rejectedconst p2 = Promise.reject(new Promise((resolve, reject) => { resolve('123')}))console.log(p2); // PromiseState rejectedp2.then(value =>{ console.log(value);},reason =>{ console.log(reason); // 失败的结果 是一个 Promise对象,这个Promise对象的状态为成功 })
4.3 all 方法
语法:
Promise.all(Array) // Array 一组Promise对象1
说明:返回一个新的 Promise,只有所有的 Promise都成功才成功,只要有一个失败了就 直接失败
第一种情况,全部都是 resolved:
const p1 = new Promise((resolve, reject) => { resolve('ok')})const p2 = Promise.resolve('success')const p3 = Promise.resolve('Oh Yeah')const result = Promise.all([p1, p2, p3])console.log('result: ', result); // PromiseState "fulfilled"// PromiseResult ['ok','success','Oh Yeah']
第二种情况,有一个是 rejected:
const p1 = new Promise((resolve, reject) => { resolve('ok')}) const p2 = Promise.reject('error info')const p3 = Promise.resolve('Oh Yeah')const result = Promise.all([p1, p2, p3])console.log('result: ', result);// PromiseState "rejected"// PromiseResult "error info"
4.4 race方法
race函数返回一个 Promise,它可以是完成( resolved),也可以是失败(rejected),这要取决于第一个完成的是哪个。
第一种情况,先完成的是 「P1 => resolved」 :
const p1 = new Promise((resolve, reject) => { resolve('ok')})const p2 = Promise.reject('error')const p3 = Promise.resolve('Oh Yeah')const result = Promise.race([p1, p2, p3])console.log(result); // PromiseState:"fulfilled" PromiseResult:"ok"
第二种情况,先完成的是 「P2 => rejected」:
//第一钟测试方式://注意:这里改变的是race方法参数的位置const p1 = new Promise((resolve, reject) => { resolve('ok')})const p2 = Promise.reject('error info')const p3 = Promise.resolve('Oh Yeah')const result = Promise.race([p2, p1, p3])console.log(result); // PromiseState:"rejected" PromiseResult:"error info"//第二钟测试方式:const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('ok') }, 1000);})const p2 = Promise.reject('error info')const p3 = Promise.resolve('Oh Yeah')const result = Promise.race([p1, p2, p3])console.log(result); // PromiseState:"rejected" PromiseResult:"error info"
4.5 实例对象的 then方法
then 方法拥有返回值,返回值是一个 Promise 对象。状态是由回调函数的执行结果来决定.
-
返回结果是非 Primose 类型,状态为成功,返回值为then方法中的返回值(下方例子中的 123即为返回值),方法中如果没有return 则为 undefined
-
如果返回值是一个 Promise 对象, 那么内部 Promise 对象返回的状态和值决定外部 then 方法的状态和值
-
如果抛出错误,则then方法状态为 rejected,值为抛出的值
提示: then方法的返回值与 async 修饰的函数的返回值一模一样
const p = new Promise((resolve, reject) => { resolve('ok')})const result = p.then(value => { // 1.返回普通数据 状态为成功,没有return状态也为成功,值为undefined // return 123; // 2.抛出错误,状态为失败,值为抛出的值 // throw '出错啦' // 3.返回一个 Promise ,内部的Promise决定外部then方法的状态及返回值 return new Promise((resolve, reject) => { // resolve() // reject() throw '出错啦!'; })}, err => { console.error(err);})console.log(result); // PromiseState: "rejected" PromiseResult: "出错啦!"
特殊情况:
then方法期望的参数是一个函数,如果不是函数则会发生 Promise穿透(值穿透),状态为上一个Promise的状态,值为上一个 Promise的值。
// 特殊情况:const p = new Promise((resolve, reject) => { resolve('ok') // reject('error')})const result = p.then(console.log(123))console.log(result); // PromiseState: "fulfilled" PromiseResult: "ok"1234567
稍微加点难度:
Promise.resolve('foo') .then(Promise.resolve('bar')) .then(function(result){ console.log(result) })
当然,输出的结果为foo。问其原因,答案如题——Promise值穿透 解释:.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。
再来一道:
Promise.resolve(1) .then(function(){return 2}) .then(Promise.resolve(3)) .then(console.log)
output: 2 解释:Promise.resolve(3) 不是函数,发生了值穿透
继续来:
Promise.resolve(1) .then(function(){return 2}) .then(function(){return Promise.resolve(3)}) .then(console.log)
output:3
五、async 和 await
5.1 async
使用 async 关键字 修饰的函数 返回值永远为 Promise 对象,这个Prmose对象的状态与then方法返回值的状态都是应用的同一种规则。
一个最简单的例子:
async function main(){ }let result = main()console.log(result); // "fulfilled" "undefined"
5.2 await
await 右侧的表达式一般为 promise对象,但也可以是其它的
-
右侧没有Promise的情况: 直接将此值作为 await的返回值
-
右侧为成功的 Promise: 返回的是 promise成功的值
-
右侧为失败的 Promise: 抛出异常,需要使用 try catch 捕获处理
注意: await 必须写在 async函数中,但 async函数中可以没有 await
async function main() { // 1.右侧没有Promise的情况: 直接将此值作为 await的返回值 let result = await 'str' console.log(result); // result => str info => fulfilled str return result // 2.右侧为成功的 Promise: 返回的是 promise成功的值 // let p1 = new Promise((resolve, reject) => { // resolve('ok') // }) // let result1 = await p1 // console.log(result1); // result => ok info => fulfilled ok // return result1 // 3.右侧为失败的 Promise: 抛出异常,需要使用 try catch 捕获处理 // let p2 = new Promise((resolve, reject) => { // reject('error') // }) // try { // let result2 = await p2 // } catch (e) { // console.log(e); // e => error info => fulfilled error // return e // }}const info = main()console.log(info);
六、async与await 结合发送 AJAX
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <!-- 引入bootstrap 的样式 --> <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.css"></head><body> <div class="container"> <h2 class="page-header">async与await封装AJAX操作</h2> <button class="btn btn-primary" id="btn">点击发送AJAX</button> </div> <script> function sendAJAX(url) { return new Promise((resolve, reject) => { xhr = new XMLHttpRequest() xhr.responseType = 'json' xhr.open('GET', url) xhr.send() xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response) } else { reject(xhr.status) } } } }) } const btn = document.querySelector('#btn') btn.addEventListener('click', async function () { let result = await sendAJAX('https://api.apiopen.top/getJoke') console.log(result); }) </script></body></html>