1. Promise是什么

1.1 理解

  • 抽象表达:

    • Promise 是一门新的技术(ES6 规范)

    • Promise 是 JS 中进行异步编程的新解决方案(旧方案是单纯使用回调函数)

  • 具体表达:

    • 从语法上来说: Promise 是一个构造函数

    • 从功能上来说: promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值

1.2 Promise的状态改变

Promise一种有三种状态,为 pending、fulfilled、rejected(未决定,履行,拒绝),同一时间只能存在一种状态,且状态一旦改变就不能再变。promise是一个构造函数,promise对象代表一项有两种可能结果(成功或失败)的任务,它还持有多个回调,出现不同结果时分别发出相应回调。

1.初始化,状态:pending ​

2.当调用resolve(成功),状态:pending=>fulfilled ​

3.当调用reject(失败),状态:pending=>rejected

状态的改变(或者说决议)不可逆,一旦决议就不能再更改。

1.3 为什么要用Promise

1)解决回调地狱

回调地狱:发送多个异步请求时,每个请求之间相互都有关联,会出现第一个请求成功后再做下一个请求的情况。我们这时候往往会用嵌套的方式来解决这种情况,但是这会形成"回调地狱"。如果处理的异步请求越多,那么回调嵌套的就越深。出现的问题:

1.代码逻辑顺序与执行顺序不一致,不利于阅读与维护。

2.异步操作顺序变更时,需要大规模的代码重构。

3.回调函数基本都是匿名函数,bug追踪困难。

doSomething(function(result) {doSomethingElse(result, function(newResult) {doThirdThing(newResult, function(finalResult) {console.log('Got the final result: ' + finalResult)}, failureCallback)}, failureCallback)
}, failureCallback)

Promise可以使用链式调用解决回调地狱的问题。

/*
使用 promise 的链式调用解决回调地狱
*/
doSomething().then(function(result) {return doSomethingElse(result)
})
.then(function(newResult) {return doThirdThing(newResult)
})
.then(function(finalResult) {console.log('Got the final result: ' + finalResult)
})
.catch(failureCallback)

2)解决异步

旧的回调函数:必须在启动异步任务前指定

promise:启动异步任务 => 返回promie对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)

1.4 工作流程

1.5 代码初体验

1)fs读取文件

const fs = require('fs');
​
//回调函数 形式
// fs.readFile('./resource/content.txt', (err, data) => {
//     // 如果出错 则抛出错误
//     if(err)  throw err;
//     //输出文件内容
//     console.log(data.toString());
// });
​
//Promise 形式
let p = new Promise((resolve , reject) => {fs.readFile('./resource/content.txt', (err, data) => {//如果出错if(err) reject(err);//如果成功resolve(data);});
});
​
//调用 then
p.then(value=>{console.log(value.toString());
}, reason=>{console.log(reason);
});

2)封装fs读取文件模块

/*** 封装一个函数 mineReadFile 读取文件内容* 参数:  path  文件路径* 返回:  promise 对象*/
function mineReadFile(path){return new Promise((resolve, reject) => {//读取文件require('fs').readFile(path, (err, data) =>{//判断if(err) reject(err);//成功resolve(data);});});
}
​
mineReadFile('./resource/content.txt')
.then(value=>{//输出文件内容console.log(value.toString());
}, reason=>{console.log(reason);
});

2. Promise API

2.1 构造函数

Promise 构造函数: Promise (excutor) {}

(1) executor 函数: 执行器 (resolve, reject) => {}

(2) resolve 函数: 内部定义成功时我们调用的函数 value => {}

(3) reject 函数: 内部定义失败时我们调用的函数 reason => {}

说明: executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行

let p = new Promise((resolve, reject) => {// 同步调用console.log(111);//修改 promise 对象的状态reject('error');
});

2.2 then

Promise.prototype.then 方法: (onResolved, onRejected) => {}

(1) onResolved 函数: 成功的回调函数 (value) => {}

(2) onRejected 函数: 失败的回调函数 (reason) => {}

说明: 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调返回一个新的 promise 对象

//调用then方法
p.then(value=>{console.log(value);
}, reason=>{console.warn(reason);
});

2.3 catch

Promise.prototype.catch 方法: (onRejected) => {}

(1) onRejected 函数: 失败的回调函数 (reason) => {}

说明: then()的语法糖, 相当于: then(undefined, onRejected)

let p = new Promise((resolve, reject) => {reject('error');
});
​
// console.log(222);
​
//执行 catch 方法
p.catch(reason => {console.log(reason);
});

2.4 resolve

Promise.resolve 方法: (value) => {}

(1) value: 成功的数据或 promise 对象

说明: 返回一个成功/失败的 Promise 对象

如果传入的参数为 非Promise类型的对象, 则返回的结果为成功Promise对象 如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果

let p1 = Promise.resolve(521);
// console.log(p1);  成功的Promise对象
//如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {// resolve('OK');reject('Error');
}));
// console.log(p2);
p2.catch(reason => {console.log(reason);
})

2.5 reject

Promise.reject 方法: (reason) => {}

(1) reason: 失败的原因

说明: 返回一个失败的 Promise 对象

// let p = Promise.reject(521);
// p2失败的结果是一个promise对象
let p2 = Promise.reject(new Promise((resolve, reject) => {resolve('OK');
}));
​
console.log(p2);

2.6 all

Promise.all 方法: (promises) => {}

(1) Promises: 包含 n 个 promise 的数组

(2) 成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

说明: 返回一个新的 Promise, 只有所有的 Promise 都成功才成功, 只要有一个失败了就直接失败

Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。

    let p1 = new Promise((resolve, reject) => {resolve('OK');})let p2 = Promise.resolve('Success');// let p2 = Promise.reject('Error');let p3 = Promise.resolve('Oh Yeah');Promise.all([p1, p2, p3]).then((result) => {console.log(result)       // [ 'OK', 'Success',"Oh Yeah" ]}).catch((error) => {console.log(error)        // error})

2.7 race

Promise.race 方法: (promises) => {}

(1) Promises: 包含 n 个 Promise 的数组

说明: 返回一个新的 Promise, 第一个完成的 Promise 的结果状态就是最终的结果状态

let p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve('OK');}, 1000);
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('Oh Yeah');
​
//调用
const result = Promise.race([p1, p2, p3]);
​
//Promise的结果为 Success,因为p2为第一个完成的promise
console.log(result);

3. Promise关键问题

1)一个 Promise 指定多个成功/失败回调函数, 都会调用吗?

当 Promise 改变为对应状态时都会调用。

let p = new Promise((resolve, reject) => {resolve('OK');
});
​
///指定回调 - 1
p.then(value => {console.log(value);
});
​
//指定回调 - 2
p.then(value => {alert(value);
});

2)改变 Promise 状态和指定回调函数谁先谁后?

(1) 都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调

(2) 如何先改状态再指定回调?

① 在执行器中直接调用 resolve()/reject()

② 延迟更长时间才调用 then()

(3) 什么时候才能得到数据?

① 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据

② 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据

3)then方法的返回结果由什么决定

(1) 简单表达:

由 then()指定的回调函数执行的结果决定

(2) 详细表达:

① 如果抛出异常, 新 Promise 变为 rejected, reason 为抛出的异常

② 如果返回的是非 Promise 的任意值, 新 Promise 变为 resolved, value 为返回的值

③ 如果返回的是另一个新 Promise, 此 Promise 的结果就会成为新 Promise 的结果

let p = new Promise((resolve, reject) => {resolve('ok');
});
//执行 then 方法
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);
});
​
console.log(result);

4)如何串联多个任务

(1) Promise 的 then()返回一个新的 promise, 可以开成 then()的链式调用

(2) 通过 then 的链式调用串连多个同步/异步任务

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);
})

5)异常穿透

(1) 当使用 Promise 的 then 链式调用时, 可以在最后指定失败的回调

(2) 前面任何操作出了异常, 都会传到最后失败的回调中处理

let p = new Promise((resolve, reject) => {setTimeout(() => {resolve('OK');// reject('Err');}, 1000);
});
​
p.then(value => {// console.log(111);throw '失败啦!';
}).then(value => {console.log(222);
}).then(value => {console.log(333);
}).catch(reason => {console.warn(reason);
});

6)中断Promise链条

(1) 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数

(2) 办法: 在回调函数中返回一个 pendding 状态的 promise 对象

let p = new Promise((resolve, reject) => {setTimeout(() => {resolve('OK');}, 1000);
});
​
p.then(value => {console.log(111);//有且只有一个方式return new Promise(() => {});
}).then(value => {console.log(222);
}).then(value => {console.log(333);
}).catch(reason => {console.warn(reason);
});

4. Async-Await

4.1 Async

  • 函数的返回值为 Promise 对象

  • Promise 对象的结果由 async 函数执行的返回值决定

  • async必须声明的是一个function

async function main(){//1. 如果返回值是一个非Promise类型的数据// return 521;//2. 如果返回的是一个Promise对象// return new Promise((resolve, reject) => {//     resolve('OK');//     // reject('Error');// });//3. 抛出异常throw "Oh NO";
}
​
let result = main();
​
console.log(result);

sync声明的函数的返回本质上是一个Promise。所以你想像正常function的返回那样,拿到返回值,你可以这样拿到返回值:

main().then(result=>{console.log(result);
})

4.2 Await

1)await 右侧的表达式一般为 promise 对象, 但也可以是其它的值

2)如果表达式是 promise 对象, await 返回的是 promise 成功的值(await会等待promise返回结果后,再继续执行)

3)如果表达式是其它值, 直接将此值作为 await 的返回值(会立即执行,不过就没有意义了)

async function main(){let p = new Promise((resolve, reject) => {// resolve('OK');reject('Error');})//1. 右侧为promise的情况// let res = await p;//2. 右侧为其他类型的数据// let res2 = await 20;// 可以用标准的try catch语法捕捉错误try{let res3 = await p;console.log(res3);}catch(e){console.log(e);}
}
​
main();

注意:

  • await 必须写在 async 函数中, 但 async 函数中可以没有 await

  • await等待的虽然是Promise对象,但不必写 .then(...),直接可以得到返回值。

  • 如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理

4.3 async/await的中断

首先我们要明确的是,Promise本身是无法中止的,Promise本身只是一个状态机,存储三个状态(pending,resolved,rejected),一旦发出请求了,必须闭环,无法取消,之前处于pending状态只是一个挂起请求的状态,并不是取消,一般不会让这种情况发生,只是用来临时中止链式的进行。

中断(终止)的本质在链式中只是挂起,并不是本质的取消Promise请求,那样是做不到的,Promise也没有cancel的状态。

不同于Promise的链式写法,写在async/await中想要中断程序就很简单了,因为语义化非常明显,其实就和一般的function写法一样,想要中断的时候,直接return一个值就行,null,空,false都是可以的。看例子:

    const setDelay = (millisecond) => {return new Promise((resolve, reject)=>{if (typeof millisecond !== 'number') reject(new Error('参数必须是number类型'));setTimeout(()=> {resolve(`我延迟了${millisecond}毫秒后输出的`)}, millisecond)})}
​let count = 6;const demo = async ()=>{const result = await setDelay(3000);console.log(result);if (count > 5) {return '我退出了,下面的不进行了';// return;// return false; // 这些写法都可以// return null;// return Promise.reject(new Error('拒绝'));}console.log(await setDelay(1000));console.log('完成了');};demo().then(result=>{console.log(result);}).catch(err=>{console.log(err);})

实质就是直接return返回了一个Promise,相当于return Promise.resolve('我退出了下面不进行了'),当然你也可以返回一个“拒绝”:return Promise.reject(new Error('拒绝'))那么就会进到错误信息里去。

async函数实质就是返回一个Promise!

4.4 async和await的结合

下面我们用两个案例演示一下:

1)读取多个文件,并输出

const fs = require('fs');
const util = require('util');
const mineReadFile = util.promisify(fs.readFile);
​
​
//async 与 await
async function main(){try{//读取第一个文件的内容let data1 = await mineReadFile('./resource/1.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);}
}
​
main();

2)发送ajax请求

    <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);}}}});}
​//段子接口地址 https://api.apiopen.top/getJokelet btn = document.querySelector('#btn');
​btn.addEventListener('click',async function(){//获取段子信息let duanzi = await sendAJAX('https://api.apiopen.top/getJoke');console.log(duanzi);});</script>

Promise和Async-Await的入门教程相关推荐

  1. es6 --- promise和async/await的区别

    首先需要了解async函数: async是Generator函数的语法糖: // 使用Generator依次读取两个文件 var fs = require('fs'); var readFile = ...

  2. 解决异步问题,教你如何写出优雅的promise和async/await,告别callback回调地狱!

    解决异步问题--promise.async/await 一.单线程和异步 1.单线程是什么 2.为什么需要异步 3.使用异步的场景 二.promise 1.promise的三种状态 2.三种状态的表现 ...

  3. setTimeout、setInterval、promise、async/await的顺序详解(多种情况,非常详细~)

    本文很长,列举的情况很多. 在阅读本文之前,如果您有充足的时间,请新建一个项目与本文一同实践. 每段代码都有对应的解释,但是自己动手尝试印象才会更深哦~ setInterval:表示多久执行一次,需要 ...

  4. promise 和 async await区别

     什么是Async/Await? async/await是写异步代码的新方式,以前的方法有回调函数和Promise. async/await是基于Promise实现的,它不能用于普通的回调函数. as ...

  5. 一眼看懂promise与async await的区别

    // promise方法let p1 = new Promise((resolve,reject) => {setTimeout(() => {resolve('我是p1')},4000) ...

  6. promise 、async/await 的原理及实现

    前言 事件循环机制 由于 javascript 引擎是采用单线程运行机制,执行耗时过大的操作时会造成页面的阻塞,为了解决页面的阻塞问题,js 将任务分为 同步任务.异步任务,随之而来的是异步带来的执行 ...

  7. 明明有了 promise ,为啥还需要 async await ?

    作者 | Angus安格斯 来源 | https://juejin.cn/post/6960855679208783903 为了让还没听说过这个特性的小伙伴们有一个大致了解,以下是一些关于该特性的简要 ...

  8. Async/Await替代Promise的6个理由

    2019独角兽企业重金招聘Python工程师标准>>> 译者按: Node.js的异步编程方式有效提高了应用性能:然而回调地狱却让人望而生畏,Promise让我们告别回调函数,写出更 ...

  9. Promise async/await的理解和用法

    Promise && async/await的理解和用法 为什么需要promise(承诺)这个东西 在之前我们处理异步函数都是用回调这个方法,回调嵌套的时候会发现 阅读性 和 调试 的 ...

  10. promise用法_JavaScript中的async/await的用法和理解

    昨天更新的是"JavaScript中的Promise使用详解",其实也就是说了下基本用法和自己对Promise的理解,可能有错误之处,也欢迎指出.今天就说一说"JavaS ...

最新文章

  1. PyTorch迎来5岁生日,创始人带领大一实习生开发:没想到会这么成功
  2. how to create view (windows)
  3. 分析 C# 2.0 新特性 -- 范型(Generics)
  4. 使用miniSIPServer搭建pjsip服务器
  5. HDU 2874 Connections between cities(LCA离线算法实现)
  6. 轮盘赌算法的java实现算例
  7. 视频工作者应该知道的几个网站
  8. go tour - Go 入门实验教程
  9. ORACLE解决表空间不释放空间
  10. 仙之侠道2玖章各个任务详情_仙之侠道2玖章
  11. 重写虫虫项目犯的低级错误
  12. xpath获取不包含某一子标签的a标签
  13. 六Elasticsearch之中文分词器插件es-ik的热更新词库
  14. 三月计算机二级,2017年3月计算机二级考试MSOffice冲刺题与答案
  15. JS -- 对于JQuery中 append 方法的理解
  16. BFS-BZOJ-1615-[Usaco2008 Mar]The Loathesome Hay Baler麻烦的干草打包机
  17. 一、ADSP-21489开发版在VisualDSP软件下的仿真器配置
  18. Visual C# 的DirectX开发系列一初识DirectX
  19. android studio 自定义控件
  20. 系统分析师真题2019试卷相关概念二

热门文章

  1. 新冠肺炎疫情预测与防控策略评价
  2. 【Flask项目】sqlalchemy原生sql查询,返回字典形式数据
  3. [ATF]-ARM级别/异常/状态切回时候的寄存器保存与恢复
  4. sql注入-error、boolean、time-based and 宽字节
  5. 与mysql的零距离接触_与MySQL的零距离接触
  6. 2020-11-16(常见加密算法统计)
  7. 006 技能数组分析和代码编写
  8. 【PAT乙级】1070 结绳 (25 分)
  9. 4.2.4 磁盘的管理
  10. docker之容器数据卷