概述

promise是异步编程的一种解决方案,比传统的解决方案—回调函数和事件—更合理更强大。

所谓的promise就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作的结果)。

Promise是一个对象,从它这里可以获取异步操作的信息, Promise提供统一的API,各种异步操作都可以用同样的方法处理。

promise的特点:

  1. 对象的状态不受外界影响,promise对象代表一个异步操作,有三种状态:pending(进行中)Fulfilled(已成功)Rejected(已失败)。只有异步操作的结果才可以决定当前是哪一种操作状态,任何其他操作都无法改变这种状态。

  2. 一旦状态改变就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变只有两种可能:从pending(进行中)变为Fulfilled(已成功)、或者从pending(进行中)变成Rejected(已失败)。状态发生改变就凝固了,不会再变,而是一直保持这个结果,这是就称为Resolved(已定型)。就算改变已经发生,再对Promise对象添加回调函数,也会立即得到这个结果,这个与event完全不同,事件的特点是,如果错过了它,再去监听是得不到结果的。

    有了promise对象,就可以将异步操作以同步操作表示出来,避免了层层嵌套的回调函数。

    Promise的缺点:

    1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消。

    2. 如果不设置回调函数,Promise内部抛出错误不会反应到外部。

    3. 当处于Pending状态时,无法得知目前进展到哪一个阶段了。

基本用法

  1. let promise = new Promise(function(res, rej) {
  2. // your code...
  3. })
  4.  
  5. promise.then(function(value) {
  6. // res
  7. }, function(error) {
  8. // rej
  9. })

执行顺序

  1. let promise = new Promise(function(resolve, reject) {
  2. // resolve, reject 才是真正的异步
  3. console.log('1')
  4. resolve()
  5. })
  6.  
  7. promise.then(() => {
  8. console.log('2')
  9. })
  10.  
  11. console.log('3')
  12.  
  13. // 1 3 2

异步加载图片

  1. // 异步加载图片
  2. function loadImageAsync(url) {
  3. return new Promise(function(resolve, reject) {
  4. let image = new Image()
  5.  
  6. iamge.onload = function () {
  7. resolve(image)
  8. }
  9.  
  10. image.onerror = function() {
  11. reject(new Error('不能加载' + url))
  12. }
  13.  
  14. iamge.src = url
  15. })
  16. }

模拟AJAX

  1. // promise对象实现AJAX
  2. // getJSON 是对XMLHttpReqest对象的封装,用于发出一个JSON数据的HTTP请求
  3. // 并返回一个Promise对象。需要注意的是,在getJSON内部,resolve函数和reject函数
  4. // 都带有参数
  5.  
  6. let getJSON = function(url) {
  7. let promise = new Promise(function(resolve, reject) {
  8. let client = new XMLHttpRequest()
  9. client.open('GET', url)
  10. client.onreadystatechange = handler
  11. client.responseType = "json"
  12. client,setRequestHeader('Accept','application/json')
  13. client.send()
  14.  
  15. function handler() {
  16. if(this.readyState !== 4) {
  17. return
  18. }
  19. if(this.status === 200) {
  20. resolve(this.response)
  21. } else {
  22. reject(new Error(this.statusText))
  23. }
  24. }
  25. })
  26. return promise
  27. }
  28.  
  29. getJSON('/posts.json').then(function(json) {
  30. console.log('Contents:' + json)
  31. },function(error) {
  32. console.log('error',error)
  33. })

promise中的promise

  1. /**
  2. * 如果resolve 和 reject 函数都带有参数,那么这些参数会被传递到回调函数。
  3. * reject函数接收Error对象的实例,表示抛出的错误
  4. * reslove 函数参数除了正确的值,还有可能是一个Promise对象
  5. */
  6.  
  7. let p1 = new Promise(function(resolve, reject) {
  8. // ...
  9. })
  10.  
  11. let p2 = new Promise(function(resolve, reject) {
  12. // ...
  13. resolve(p1)
  14. })
  15. /**p1 和 p2 都是Promise的实例,但是p2的resolve方法将p1作为参数
  16. * 即一个异步操作的结果是返回另一个异步操作。
  17. * p1的状态传递给p2.p1的状态决定了p2的状态
  18. */
  1. let p1 = new Promise(function(resolve, reject) {
  2. setTimeout(() => reject(new Error('fail')), 3000)
  3. })
  4.  
  5. let p2 = new Promise(function(resolve, reject) {
  6. setTimeout(() => resolve(p1), 1000)
  7. })
  8.  
  9. p2.then(result => console.log(result)).catch(error => console.log(error))
  10.  
  11. /**
  12. * p1 是 一个Promise,3s之后变成rejected。
  13. * p2 的状态在1s后改变,resolve方法返回的是p1
  14. */

Promise.prototype.then()

作用:为了Promise实例添加状态改变时的回调。

  1. promise.then(function(value) {
  2. // res
  3. }, function(error) {
  4. // rej
  5. })
  6.  
  7. // then方法有两个参数,第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。
  8. // then返回的是一个新的Promise实例

Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误的回调函数。

另外then方法指定的回调函数如果在运行中抛出错误,也会被catch捕获。

一般来说,不要在then方法中定义Rejected状态的回调(即then的第二个参数),而应该总是使用catch方法。

  1. p2.then(result => console.log(result))
  2. .catch(error => console.log(error))
  3. // .catch 处理 p2 和前一个回调函数运行时发生的错误
  4.  
  5. // ====> 等同于
  6. p2.then(result => console.log(result))
  7. .then(null, err => console.log(err))

Promise.all()

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

  1. const p = Promise.all([p1, p2, p3]);
  2. // Promise.all方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法, 将参数转为 Promise 实例,再进一步处理。

p的状态由p1、p2、p3决定,分成两种情况。

1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

  1. const databasePromise = connectDatabase();
  2.  
  3. const booksPromise = databasePromise.then(findAllBooks);
  4.  
  5. const userPromise = databasePromise.then(getCurrentUser);
  6.  
  7. // booksPromise和userPromise是两个异步操作,只有等到它们的结果都返回了,才会触发pickTopRecommentations这个回调函数。
  8. // 如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法
  9. Promise.all([booksPromise, userPromise]).then(([books, user]) => pickTopRecommentations(books, user));

async函数

ES7标准引入了async函数,使得异步操作变得简单,——async就是Generator的语法糖。

用法

async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,

一旦遇上await就会先返回,等到异步操作完成,再接着执行函数体内后面

  1. async function getStockPriceByName(name) {
  2. const symbol = await getStockSymbol(name);
  3. const stockPrice = await getStockPrice(symbol);
  4. return stockPrice;
  5. }
  6.  
  7. getStockPriceByName('goog').then(function (result) {
  8. console.log(result);
  9. });
  10. // 1、函数前面的async关键字表明该函数内部有异步操作。
  11. // 2、调用该函数会立即返回一个Promise对象

指定多少毫秒输出一个值

  1. function timeout(ms) {
  2. return new Promise((resolve) => {
  3. setTimeout(resolve, ms);
  4. });
  5. }
  6.  
  7. async function asyncPrint(value, ms) {
  8. // 一旦遇上await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
  9. await timeout(ms);
  10. console.log(value);
  11. }
  12.  
  13. asyncPrint('hello world', 3000); // 3s之后返回 'hello world'
  14.  
  15. /** ------------- 改写 ------------------ */
  16. async function timeout(ms) {
  17. await new Promise((resolve) => {
  18. setTimeout(resolve, ms);
  19. });
  20. }
  21.  
  22. async function asyncPrint(value, ms) {
  23. await timeout(ms);
  24. console.log(value);
  25. }
  26.  
  27. asyncPrint('hello world', 3000);

语法

async函数返回一个promise对象,async函数内部return语句返回的值,会成为then方法回调函数的参数。

Promise 对象的状态变化

async函数返回的 Promise对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。

也就是说,只有async函数内部的所有异步操作执行完,才会执行then方法指定的回调函数。

正常情况下,await命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve的 Promise 对象。

  1. async function f() {
  2. return await 123;
  3. }
  4.  
  5. // await命令的参数是数值123,它被转成 Promise 对象,并立即resolve。
  6.  
  7. f().then(v => console.log(v)) //

await命令后面的 Promise 对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。

  1. async function f() {
  2. await Promise.reject('出错了');
  3. }
  4.  
  5. f().then(v => console.log(v))
  6. .catch(e => console.log(e)) // 出错了

只要一个await语句后面的 Promise 变为reject,那么整个async函数都会中断执行。

  1. async function f() {
  2. await Promise.reject('出错了');
  3. await Promise.resolve('hello world'); // 不会执行
  4. }
  5. // 第二个await不会执行

现在,需要在第一个异步操作失败,也不要中断后面的异步操作。

这是可以将第一个await放在try...catch...结构里面,这样不管这个异步操作返回的结果是resolve还是reject都会执行之后的await操作。

  1. async function f() {
  2. try {
  3. await Promise.reject('出错了');
  4. } catch (e) {
  5. console.log(e);
  6. }
  7. return await Promise.resolve('hello world');
  8. }
  9.  
  10. f().then(v => console.log(v)) // hello world

还有一种方法,就是在第一个await之后使用catch接收失败的回调,处理前面出现的错误。

  1. async function f() {
  2. await Promise.reject('出错了').catch(e => console.log(e));
  3. return await Promise.resolve('hello world');
  4. }
  5.  
  6. f().then(v => console.log(v)) // 出错了 // hello world

错误处理

如果await后面的异步操作出错了,那么等同于async函数返回的Promise对象被reject了。

  1. async function f() {
  2. await new Promise(function (resolve, reject) {
  3. throw new Error('出错了');
  4. });
  5. }
  6.  
  7. f().then(v => console.log(v)).catch(e => console.log(e)) // Error:出错了
  8. // async函数f执行后,await后面的 Promise 对象会抛出一个错误对象,
  9. // 导致catch方法的回调函数被调用,它的参数就是抛出的错误对象。
  1. // 有多个await命令,则可以统一放在try...catch...结构中
  2. async function main() {
  3. try {
  4. const val1 = await firstStep();
  5. const val2 = await secondStep(val1);
  6. const val3 = await thirdStep(val1, val2);
  7.  
  8. console.log('Final: ', val3);
  9. } catch (err) {
  10. console.error(err);
  11. }
  12. }

await使用注意点

第一点,前面已经说过,await命令后面的Promise对象,运行结果可能是rejected,所以好把await命令放在try...catch代码块中。

  1. async function myFunction() {
  2. try {
  3. await somethingThatReturnsAPromise();
  4. } catch (err) {
  5. console.log(err);
  6. }
  7. }
  8.  
  9. // 另一种写法
  10.  
  11. async function myFunction() {
  12. await somethingThatReturnsAPromise().catch(function (err) {
  13. console.log(err);
  14. });
  15. }

第二点,多个await命令后面的异步操作,如果不存在继发关系,好让它们同时触发

  1. // 写法一
  2. let [foo, bar] = await Promise.all([getFoo(), getBar()]);
  3.  
  4. // 写法二
  5. let fooPromise = getFoo();
  6. let barPromise = getBar();
  7. let foo = await fooPromise;
  8. let bar = await barPromise;

第三点,await命令只能用在async函数之中,如果用在普通函数,就会报错。

  1. async function dbFuc(db) {
  2. let docs = [{}, {}, {}];
  3. // 报错
  4. docs.forEach(function (doc) {
  5. await db.post(doc);
  6. });
  7. }
  8.  
  9. // 如果将forEach方法的参数改成async函数,也有问题。
  10. function dbFuc(db) { //这里不需要 async
  11. let docs = [{}, {}, {}];
  12. // 可能得到错误结果
  13. docs.forEach(async function (doc) {
  14. await db.post(doc);
  15. });
  16. }
  17. // 原因是这时三个db.post操作将是并发执行,也就是同时执行,而不是继发执行。正确的写法是采用for循环。
  1. async function dbFuc(db) {
  2. let docs = [{}, {}, {}];
  3. for (let doc of docs) {
  4. await db.post(doc);
  5. }
  6. }
  1. // 如果确实希望多个请求并发执行,可以使用Promise.all方法。当三个请求都会resolved时,下面两种写法效果相同。
  2.  
  3. async function dbFuc(db) {
  4. let docs = [{}, {}, {}];
  5. let promises = docs.map((doc) => db.post(doc));
  6.  
  7. let results = await Promise.all(promises);
  8. console.log(results);
  9. }
  10.  
  11. // 或者使用下面的写法
  12.  
  13. async function dbFuc(db) {
  14. let docs = [{}, {}, {}];
  15. let promises = docs.map((doc) => db.post(doc));
  16.  
  17. let results = [];
  18. for (let promise of promises) {
  19. results.push(await promise);
  20. }
  21. console.log(results);
  22. }

实例

  1. var pro = new Promise(function(){
  2. var a = 1;
  3. })
  4. console.log(pro);

读文件的方法

  1. const fs = require('fs')
  2.  
  3. // 总结:只要 new 了一个具体的异步操作,这个异步操作被创建的一瞬间,就会立即执行;
  4. function readFileByPath(fpath) {
  5. const p = new Promise(function() {
  6. fs.readFile(fpath, 'utf-8', (err, result) => {
  7. if (err) return console.log('读文件失败:' + err.message)
  8. console.log(result)
  9. })
  10. })
  11. }
  12.  
  13. readFileByPath('./files/3.txt') // 文件必须存在

使用.then()进一步封装读文件的操作

封装原则:不要在方法内部显示结果,要把结果返回给调用者,不要提调用者做决定!!

使用.catch()进一步封装读文件的操作

  1. const fs = require('fs')
  2.  
  3. function readFileByPath(fPath) {
  4. return new Promise(function(resolve,reject){
  5. fs.readFile(fPath, 'utf-8', (err, result) => {
  6. if (err) return reject(arr)
  7. resolve(result)
  8. })
  9. })
  10. }
  11.  
  12. // 一般.then()方法中,失败的回调可以省略,但是省略以后读取文件失败时,无法接收结果
  13. // 这是,我们可以使用.catch() 来指定失败的回调
  14. /* --------------读一个-------------------- */
  15. readFileByPath('./files/1.txt')
  16. .then(function(result) {
  17. console.log(result)
  18. })
  19. .catch(err => console.log(err.message))
  20.  
  21. /* ---------------读多个----------------------- */
  22. readFileByPath('./files/1.txt')
  23. .then(function(result){
  24. console.log(result)
  25. return readFileByPath('./files/2.txt')
  26. })
  27. .then(function(result){
  28. console.log(result)
  29. return readFileByPath('./files/3.txt')
  30. })
  31. .catch(err => console.log(err.message))

async和await

  1. const fs = require('fs')
  2.  
  3. function readFileByPath(fPath) {
  4. return new Promise(function(resolve,reject){
  5. fs.readFile(fPath, 'utf-8', (err, result) => {
  6. if (err) return reject(arr)
  7. resolve(result)
  8. })
  9. })
  10. }
  11.  
  12. // async 用来修饰异步方法
  13. // await 只能用在被 async 修饰的方法中
  14. // 同时,await 是用来修饰 Promise 实例对象的;简化promise对象
  15.  
  16. console.log("开始");
  17. async function readAll () {
  18. console.log("方法头部");
  19. const result1 = await readFileByPath('./files/1.txt')
  20. console.log(result1)
  21. const result2 = await readFileByPath('./files/2.txt')
  22. console.log(result2)
  23. const result3 = await readFileByPath('./files/3.txt')
  24. console.log(result3)
  25. console.log("方法尾部");
  26. }
  27. console.log("结束");
  28.  
  29. readAll()

运行顺序:

原因:

/* async修饰的 readAll()方法异步方法,在调用这个readAll()方法的时候,js主线程进入这个方法
这时,还是主线程在执行,输出“方法头部”,然后在 await是异步操作,这是主线程就退出这个方法,
执行“结束”,然后就是异步的方法顺序执行 */

ES系列之Promise async 和 await的更多相关文章

  1. promise, async和await

    最开始实现异步的方法:回调函数 method1(function(err, result) { if (err) { throw err; } method2(function(err, result ...

  2. promise以及async、await学习总结

    Promise/async.await帮我们解决了什么 它给我们提供了一种新的异步编程解决方案,同时避免了困扰已久的回调地狱 // 异步的处理可能会产生这样的回调地狱(第二个异步操作和第一个异步的结果 ...

  3. ES 7 async/await Promise

    如何添加SSL证书实现https请求 https://blog.csdn.net/lupengfei1009/article/details/76828190/ ES 7     async/awai ...

  4. C#基础系列——异步编程初探:async和await

    前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法.确实,没有异步的多线程是单调的.乏味的,async和await是出现在C#5.0之后,它的出现给了 ...

  5. Promise,Async,await简介

    Promise 对象 转载:http://wiki.jikexueyuan.com/project/es6/promise.html 基本用法 ES6 原生提供了 Promise 对象.所谓 Prom ...

  6. promise async await使用

    1.Promise (名字含义:promise为承诺,表示其他手段无法改变) Promise 对象代表一个异步操作,其不受外界影响,有三种状态: Pending(进行中.未完成的) Resolved( ...

  7. 详解promise、async和await的执行顺序

    1.题目和答案 一道题题目:下面这段promise.async和await代码,请问控制台打印的顺序? async function async1(){ console.log('async1 sta ...

  8. Promise、async、await在Egret的简单应用

    Egret Engnie 5.1.10 Egret Wing 4.1.5 一.Promise.async.await相关知识 Promise介绍 阮一峰 async函数 阮一峰 具体和详细的说明用法可 ...

  9. 反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) C#中缓存的使用 C#操作redis WPF 控件库——可拖动选项卡的TabControl 【Bootstrap系列】详解Bootstrap-table AutoFac event 和delegate的分别 常见的异步方式async 和 await C# Task用法 c#源码的执行过程

    反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑)   背景介绍: 为了平衡社区成员的贡献和索取,一起帮引入了帮帮币.当用户积分(帮帮点)达到一定数额之后,就会“掉落”一定数量的“帮帮 ...

随机推荐

  1. 【转】Spark Streaming 实时计算在甜橙金融监控系统中的应用及优化

    系统架构介绍 整个实时监控系统的架构是先由 Flume 收集服务器产生的日志 Log 和前端埋点数据, 然后实时把这些信息发送到 Kafka 分布式发布订阅消息系统,接着由 Spark Streami ...

  2. Job for nginx.service failed because the control process exited with error code. See “systemctl stat

    启动nginx服务时如果遇到这个错误 Job for nginx.service failed because the control process exited with error code. ...

  3. jdbc oracle 连接串

    jdbc.url配置为: jdbc:oracle:thin:@xxx.xx.xx.xx:1521:orclpdb 报错: java.sql.SQLException: Listenerrefused ...

  4. POJ1723 SOLDIERS 兄弟连

    SOLDIERS 有一个性质:在一个长为n的序列a中找一个数 \(a_k\) 使得 \(\sum\limits_{i=1}^n abs(a_i-a_k)\) 最小,则 \(a_k\) 是a的中位数. ...

  5. HDU - 6181 Two Paths(次短路)

    题意:求次短路. 分析:关键是情况讨论. LL tmpd = x.d + e.dist; 以下情况对应的更新结果 1.tmpd(2) < 最短路(3) < 次短路(4)-------> ...

  6. .Net 经典案例

    1.捕捉一只小可爱 using System; using System.Collections.Generic; using System.Linq; using System.Text; usin ...

  7. 151-PHP nl2br函数(二)

    <?php $str="h\nt\nm\nl"; //定义一个多处换行的字串 echo "未处理前的输出形式:<br />{$str}"; $ ...

  8. 112-PHP类变量之间的赋值标识为同一个对象(二)

    <?php class mao{ //定义猫类 public $age=0; //定义多个属性并初始化 public $weight=50; public $color='white'; } $ ...

  9. C# winform中ListView用法

    this.listView1.GridLines = true; //显示表格线 this.listView1.View = View.Details;//显示表格细节 this.listView1. ...

  10. 箭头函数this

    箭头函数的this值是由包含它的函数(非箭头函数)来决定的,与包含的函数的this指向一致,如果包裹它的不是函数(直到找到最外层)则this指向全局对象 并且箭头函数的this是固定的,由定义它时所在 ...