引言:
感觉promise的思想非常有意思,为了代码的可读性和可维护性提供了一种新的异步编程方法,接触了一段时间的工程代码也让我体会到代码可维护,可拓展的必要性。
为什么要用Promise?
对于异步操作,例如文件读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var fs = require('fs')
fs.readFile('./a.txt', 'utf-8', function(err, data) { if (err) { throw err; } console.log(data); }) fs.readFile('./b.txt', 'utf-8', function(err, data) { if (err) { throw err; } console.log(data); }) fs.readFile('./c.txt', 'utf-8', function(err, data) { if (err) { throw err; } console.log(data); })
|
输出结果:

由于读取文件为异步操作,输出的结果不确定。
为了a -> b -> c依次读取文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| fs.readFile('./src/a.txt', 'utf-8', function(err, data) { if (err) { throw err; } console.log(data); fs.readFile('./src/b.txt', 'utf-8', function(err, data) { if (err) { throw err; } console.log(data); fs.readFile('./src/c.txt', 'utf-8', function(err, data) { if (err) { throw err; } console.log(data); }) }) })
|
回调函数嵌套回调函数,导致了回调地狱,带来的问题也很明显,不便于维护,阅读,异常处理。
使用promise:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| var fs = require('fs') function myReadFile(url) { return new Promise((resolve, reject) => { fs.readFile(url, 'utf-8', function(err, data){ if (err) { reject(err); } resolve(data); }) }) } myReadFile('./a.txt') .then(value=>{ console.log(value); return myReadFile('./b.txt'); }) .then(value=> { console.log(value); return myReadFile('./c.txt'); }) .then(value=>{ console.log(value); }) .catch(reason=> { console.log('reason', reason); });
|
由于异常穿透,异常只需要在最后捕获,代码的可读性和维护性也更高,解决了回调地狱的问题。
Promise的工作流程

如何改变promise对象的状态?
1 2 3 4 5 6 7 8
| let p = new Promise((resolve, reject) => { });
|



Promise Api
Promise.prototype.then
方法返回一个 ```Promise```。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| ##### Promise.prototype.then()返回的新 promise 的结果状态由什么决定?
**由 then()指定的回调函数执行的结果决定**
1. 如果抛出异常, 新 promise 变为 rejected, *[[PromiseResult]]*为抛出的异常
2. 如果返回的是非 promise 的任意值, 或者没有返回值(相当于返回undefined),新 promise 变为 resolved, *[[PromiseResult]]*为返回的值
3. ***如果返回的是另一个新 promise, 此promise的状态成为新promise的状态,此 promise 的[[PromiseResult]]就会成为新 promise 的[[PromiseResult]]***
```javascript let result = p.then(value => { // console.log(value); //1. 抛出错误 // throw '出了问题'; //2. 返回结果是非 Promise 类型的对象 // return 521; //3. 返回结果是 Promise 对象 // return new Promise((resolve, reject) => { // // resolve('success'); // reject('error'); // }); }, reason => { console.warn(reason); });
|
then的链式调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| let p = new Promise((resolve, reject) => { setTimeout(() => { resolve('OK'); }, 1000); });
p.then(value => { return new Promise((resolve, reject) => { resolve("success"); }); }).then(value => { console.log(value); }).then(value => { console.log(value); })
|
Promise.resolve
- 如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象
- 如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
1 2 3 4 5 6 7 8 9
| let p1 = Promise.resolve(521); let p2 = Promise.resolve(new Promise((resolve, reject) => { reject('Error'); }));
p2.catch(reason => { console.log(reason); })
|
Promise.reject
Promise.reject(reason)方法返回一个失败的promise对象(无论reason是什么)
1 2 3 4
| let p3 = Promise.reject(new Promise((resolve, reject) => { resolve('OK'); })); console.log(p3);
|
输出:

Promise.prototype.catch
catch() 方法返回一个Promise,只处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected)
相同。
Promise关键问题
Promise可以执行多个成功/失败的回调
1 2 3 4 5 6 7 8 9 10 11 12
| let p = new Promise((resolve, reject) => { resolve('OK'); });
p.then(value => { console.log(value); });
p.then(value => { alert(value); });
|
异常穿透
当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调。
前面任何操作出了异常, 都会传到最后失败的回调中处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| let p = new Promise((resolve, reject) => { setTimeout(() => { reject('Err'); }, 1000); });
p.then(value => { throw '失败啦!'; }).then(value => { console.log(222); }).then(value => { console.log(333); }).catch(reason => { console.warn(reason); }).then(value => { console.log(value) console.log('hyz') })
|
如何中断Promise链?
添加一个pendding状态的promise
1 2 3 4 5 6 7 8 9 10 11
| p.then(value => { console.log(111); return new Promise(() => {}); }).then(value => { console.log(222); }).then(value => { console.log(333); }).catch(reason => { console.warn(reason); });
|
Async与Await
Async/Await 是js异步编程的终极解决方案
Async函数
返回值规则与Promise.prototype.then()相同
- 函数的返回值为 promise 对象
- promise 对象的结果由 async 函数执行的返回值决定
1 2 3 4 5 6 7 8 9 10 11
| async function main(){ throw "Oh NO"; }
|
Await表达式
- await 右侧的表达式一般为 promise 对象, 但也可以是其它的值
- 如果表达式是 promise 对象, await 返回的是 promise 成功的值([[PromiseResult]])
- 如果表达式是其它值, 直接将此值作为 await 的返回值
mentoon:
- await 必须写在 async 函数中, 但 async 函数中可以没有 await
- 如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try…catch 捕获处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| async function main(){ let p = new Promise((resolve, reject) => { reject('Error'); }) try{ let res3 = await p; }catch(e){ console.log(e); } } main();
|
实践:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| fs.readFile('./resource/1.html', (err, data1) => { if(err) throw err; fs.readFile('./resource/2.html', (err, data2) => { if(err) throw err; fs.readFile('./resource/3.html', (err, data3) => { if(err) throw err; console.log(data1 + data2 + data3); }); }); });
async function main(){ try{ let data1 = await mineReadFile('./resource/1x.html'); let data2 = await mineReadFile('./resource/2.html'); let data3 = await mineReadFile('./resource/3.html'); console.log(data1 + data2 + data3); }catch(e){ console.log(e.code); } }
|
async await 封装ajax
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <body> <button id="btn">点击获取段子</button> <script> function sendAJAX(url){ return new Promise((resolve, reject) => { const 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); } } } }); }
let btn = document.querySelector('#btn');
btn.addEventListener('click',async function(){ let duanzi = await sendAJAX('https://api.apiopen.top/getJoke'); console.log(duanzi); }); </script> </body>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| async function async1() { console.log('async1 start') await async2() console.log('async1 end') }
async function async2() { console.log('async2') }
console.log('script start')
setTimeout(function () { console.log('setTimeout') }, 0)
async1();
new Promise(function (resolve) { console.log('promise1') resolve() }).then(function () { console.log('promise2') })
console.log('script end')
|