ES系列之Promise async 和 await
概述
promise
是异步编程的一种解决方案,比传统的解决方案—回调函数和事件—更合理更强大。
所谓的promise
就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作的结果)。
Promise
是一个对象,从它这里可以获取异步操作的信息, Promise
提供统一的API,各种异步操作都可以用同样的方法处理。
promise
的特点:
对象的状态不受外界影响,
promise
对象代表一个异步操作,有三种状态:pending(进行中)
、Fulfilled(已成功)
、Rejected(已失败)
。只有异步操作的结果才可以决定当前是哪一种操作状态,任何其他操作都无法改变这种状态。一旦状态改变就不会再变,任何时候都可以得到这个结果。
Promise
对象的状态改变只有两种可能:从pending(进行中)
变为Fulfilled(已成功)
、或者从pending(进行中)
变成Rejected(已失败)
。状态发生改变就凝固了,不会再变,而是一直保持这个结果,这是就称为Resolved(已定型)
。就算改变已经发生,再对Promise
对象添加回调函数,也会立即得到这个结果,这个与event
完全不同,事件的特点是,如果错过了它,再去监听是得不到结果的。有了
promise
对象,就可以将异步操作以同步操作表示出来,避免了层层嵌套的回调函数。Promise
的缺点:无法取消
Promise
,一旦新建它就会立即执行,无法中途取消。如果不设置回调函数,
Promise
内部抛出错误不会反应到外部。当处于
Pending
状态时,无法得知目前进展到哪一个阶段了。
基本用法
- let promise = new Promise(function(res, rej) {
- // your code...
- })
- promise.then(function(value) {
- // res
- }, function(error) {
- // rej
- })
执行顺序
- let promise = new Promise(function(resolve, reject) {
- // resolve, reject 才是真正的异步
- console.log('1')
- resolve()
- })
- promise.then(() => {
- console.log('2')
- })
- console.log('3')
- // 1 3 2
异步加载图片
- // 异步加载图片
- function loadImageAsync(url) {
- return new Promise(function(resolve, reject) {
- let image = new Image()
- iamge.onload = function () {
- resolve(image)
- }
- image.onerror = function() {
- reject(new Error('不能加载' + url))
- }
- iamge.src = url
- })
- }
模拟AJAX
- // promise对象实现AJAX
- // getJSON 是对XMLHttpReqest对象的封装,用于发出一个JSON数据的HTTP请求
- // 并返回一个Promise对象。需要注意的是,在getJSON内部,resolve函数和reject函数
- // 都带有参数
- let getJSON = function(url) {
- let promise = new Promise(function(resolve, reject) {
- let client = new XMLHttpRequest()
- client.open('GET', url)
- client.onreadystatechange = handler
- client.responseType = "json"
- client,setRequestHeader('Accept','application/json')
- client.send()
- function handler() {
- if(this.readyState !== 4) {
- return
- }
- if(this.status === 200) {
- resolve(this.response)
- } else {
- reject(new Error(this.statusText))
- }
- }
- })
- return promise
- }
- getJSON('/posts.json').then(function(json) {
- console.log('Contents:' + json)
- },function(error) {
- console.log('error',error)
- })
promise中的promise
- /**
- * 如果resolve 和 reject 函数都带有参数,那么这些参数会被传递到回调函数。
- * reject函数接收Error对象的实例,表示抛出的错误
- * reslove 函数参数除了正确的值,还有可能是一个Promise对象
- */
- let p1 = new Promise(function(resolve, reject) {
- // ...
- })
- let p2 = new Promise(function(resolve, reject) {
- // ...
- resolve(p1)
- })
- /**p1 和 p2 都是Promise的实例,但是p2的resolve方法将p1作为参数
- * 即一个异步操作的结果是返回另一个异步操作。
- * p1的状态传递给p2.p1的状态决定了p2的状态
- */
- let p1 = new Promise(function(resolve, reject) {
- setTimeout(() => reject(new Error('fail')), 3000)
- })
- let p2 = new Promise(function(resolve, reject) {
- setTimeout(() => resolve(p1), 1000)
- })
- p2.then(result => console.log(result)).catch(error => console.log(error))
- /**
- * p1 是 一个Promise,3s之后变成rejected。
- * p2 的状态在1s后改变,resolve方法返回的是p1
- */
Promise.prototype.then()
作用:为了Promise实例添加状态改变时的回调。
- promise.then(function(value) {
- // res
- }, function(error) {
- // rej
- })
- // then方法有两个参数,第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。
- // then返回的是一个新的Promise实例
Promise.prototype.catch()
Promise.prototype.catch
方法是.then(null, rejection)
的别名,用于指定发生错误的回调函数。
另外then
方法指定的回调函数如果在运行中抛出错误,也会被catch
捕获。
一般来说,不要在then
方法中定义Rejected状态的回调(即then
的第二个参数),而应该总是使用catch
方法。
- p2.then(result => console.log(result))
- .catch(error => console.log(error))
- // .catch 处理 p2 和前一个回调函数运行时发生的错误
- // ====> 等同于
- p2.then(result => console.log(result))
- .then(null, err => console.log(err))
Promise.all()
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
- const p = Promise.all([p1, p2, p3]);
- // 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的回调函数。
- const databasePromise = connectDatabase();
- const booksPromise = databasePromise.then(findAllBooks);
- const userPromise = databasePromise.then(getCurrentUser);
- // booksPromise和userPromise是两个异步操作,只有等到它们的结果都返回了,才会触发pickTopRecommentations这个回调函数。
- // 如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法
- Promise.all([booksPromise, userPromise]).then(([books, user]) => pickTopRecommentations(books, user));
async函数
ES7标准引入了async
函数,使得异步操作变得简单,——async
就是Generator
的语法糖。
用法
async
函数返回一个Promise
对象,可以使用then
方法添加回调函数。当函数执行的时候,
一旦遇上await
就会先返回,等到异步操作完成,再接着执行函数体内后面
- async function getStockPriceByName(name) {
- const symbol = await getStockSymbol(name);
- const stockPrice = await getStockPrice(symbol);
- return stockPrice;
- }
- getStockPriceByName('goog').then(function (result) {
- console.log(result);
- });
- // 1、函数前面的async关键字表明该函数内部有异步操作。
- // 2、调用该函数会立即返回一个Promise对象
指定多少毫秒输出一个值
- function timeout(ms) {
- return new Promise((resolve) => {
- setTimeout(resolve, ms);
- });
- }
- async function asyncPrint(value, ms) {
- // 一旦遇上await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
- await timeout(ms);
- console.log(value);
- }
- asyncPrint('hello world', 3000); // 3s之后返回 'hello world'
- /** ------------- 改写 ------------------ */
- async function timeout(ms) {
- await new Promise((resolve) => {
- setTimeout(resolve, ms);
- });
- }
- async function asyncPrint(value, ms) {
- await timeout(ms);
- console.log(value);
- }
- asyncPrint('hello world', 3000);
语法
async
函数返回一个promise
对象,async
函数内部return语句返回的值,会成为then
方法回调函数的参数。
Promise 对象的状态变化
async
函数返回的 Promise
对象,必须等到内部所有await
命令后面的 Promise
对象执行完,才会发生状态改变,除非遇到return
语句或者抛出错误。
也就是说,只有async
函数内部的所有异步操作执行完,才会执行then
方法指定的回调函数。
正常情况下,await命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve的 Promise 对象。
- async function f() {
- return await 123;
- }
- // await命令的参数是数值123,它被转成 Promise 对象,并立即resolve。
- f().then(v => console.log(v)) //
await命令后面的 Promise 对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。
- async function f() {
- await Promise.reject('出错了');
- }
- f().then(v => console.log(v))
- .catch(e => console.log(e)) // 出错了
只要一个await语句后面的 Promise 变为reject,那么整个async函数都会中断执行。
- async function f() {
- await Promise.reject('出错了');
- await Promise.resolve('hello world'); // 不会执行
- }
- // 第二个await不会执行
现在,需要在第一个异步操作失败,也不要中断后面的异步操作。
这是可以将第一个await
放在try...catch...
结构里面,这样不管这个异步操作返回的结果是resolve
还是reject
都会执行之后的await
操作。
- async function f() {
- try {
- await Promise.reject('出错了');
- } catch (e) {
- console.log(e);
- }
- return await Promise.resolve('hello world');
- }
- f().then(v => console.log(v)) // hello world
还有一种方法,就是在第一个await
之后使用catch
接收失败的回调,处理前面出现的错误。
- async function f() {
- await Promise.reject('出错了').catch(e => console.log(e));
- return await Promise.resolve('hello world');
- }
- f().then(v => console.log(v)) // 出错了 // hello world
错误处理
如果await后面的异步操作出错了,那么等同于async函数返回的Promise对象被reject了。
- async function f() {
- await new Promise(function (resolve, reject) {
- throw new Error('出错了');
- });
- }
- f().then(v => console.log(v)).catch(e => console.log(e)) // Error:出错了
- // async函数f执行后,await后面的 Promise 对象会抛出一个错误对象,
- // 导致catch方法的回调函数被调用,它的参数就是抛出的错误对象。
- // 有多个await命令,则可以统一放在try...catch...结构中
- async function main() {
- try {
- const val1 = await firstStep();
- const val2 = await secondStep(val1);
- const val3 = await thirdStep(val1, val2);
- console.log('Final: ', val3);
- } catch (err) {
- console.error(err);
- }
- }
await使用注意点
第一点,前面已经说过,await命令后面的Promise对象,运行结果可能是rejected,所以好把await命令放在try...catch代码块中。
- async function myFunction() {
- try {
- await somethingThatReturnsAPromise();
- } catch (err) {
- console.log(err);
- }
- }
- // 另一种写法
- async function myFunction() {
- await somethingThatReturnsAPromise().catch(function (err) {
- console.log(err);
- });
- }
第二点,多个await命令后面的异步操作,如果不存在继发关系,好让它们同时触发
- // 写法一
- let [foo, bar] = await Promise.all([getFoo(), getBar()]);
- // 写法二
- let fooPromise = getFoo();
- let barPromise = getBar();
- let foo = await fooPromise;
- let bar = await barPromise;
第三点,await命令只能用在async函数之中,如果用在普通函数,就会报错。
- async function dbFuc(db) {
- let docs = [{}, {}, {}];
- // 报错
- docs.forEach(function (doc) {
- await db.post(doc);
- });
- }
- // 如果将forEach方法的参数改成async函数,也有问题。
- function dbFuc(db) { //这里不需要 async
- let docs = [{}, {}, {}];
- // 可能得到错误结果
- docs.forEach(async function (doc) {
- await db.post(doc);
- });
- }
- // 原因是这时三个db.post操作将是并发执行,也就是同时执行,而不是继发执行。正确的写法是采用for循环。
- async function dbFuc(db) {
- let docs = [{}, {}, {}];
- for (let doc of docs) {
- await db.post(doc);
- }
- }
- // 如果确实希望多个请求并发执行,可以使用Promise.all方法。当三个请求都会resolved时,下面两种写法效果相同。
- async function dbFuc(db) {
- let docs = [{}, {}, {}];
- let promises = docs.map((doc) => db.post(doc));
- let results = await Promise.all(promises);
- console.log(results);
- }
- // 或者使用下面的写法
- async function dbFuc(db) {
- let docs = [{}, {}, {}];
- let promises = docs.map((doc) => db.post(doc));
- let results = [];
- for (let promise of promises) {
- results.push(await promise);
- }
- console.log(results);
- }
实例
- var pro = new Promise(function(){
- var a = 1;
- })
- console.log(pro);
读文件的方法
- const fs = require('fs')
- // 总结:只要 new 了一个具体的异步操作,这个异步操作被创建的一瞬间,就会立即执行;
- function readFileByPath(fpath) {
- const p = new Promise(function() {
- fs.readFile(fpath, 'utf-8', (err, result) => {
- if (err) return console.log('读文件失败:' + err.message)
- console.log(result)
- })
- })
- }
- readFileByPath('./files/3.txt') // 文件必须存在
使用.then()进一步封装读文件的操作
封装原则:不要在方法内部显示结果,要把结果返回给调用者,不要提调用者做决定!!
使用.catch()进一步封装读文件的操作
- const fs = require('fs')
- function readFileByPath(fPath) {
- return new Promise(function(resolve,reject){
- fs.readFile(fPath, 'utf-8', (err, result) => {
- if (err) return reject(arr)
- resolve(result)
- })
- })
- }
- // 一般.then()方法中,失败的回调可以省略,但是省略以后读取文件失败时,无法接收结果
- // 这是,我们可以使用.catch() 来指定失败的回调
- /* --------------读一个-------------------- */
- readFileByPath('./files/1.txt')
- .then(function(result) {
- console.log(result)
- })
- .catch(err => console.log(err.message))
- /* ---------------读多个----------------------- */
- readFileByPath('./files/1.txt')
- .then(function(result){
- console.log(result)
- return readFileByPath('./files/2.txt')
- })
- .then(function(result){
- console.log(result)
- return readFileByPath('./files/3.txt')
- })
- .catch(err => console.log(err.message))
async和await
- const fs = require('fs')
- function readFileByPath(fPath) {
- return new Promise(function(resolve,reject){
- fs.readFile(fPath, 'utf-8', (err, result) => {
- if (err) return reject(arr)
- resolve(result)
- })
- })
- }
- // async 用来修饰异步方法
- // await 只能用在被 async 修饰的方法中
- // 同时,await 是用来修饰 Promise 实例对象的;简化promise对象
- console.log("开始");
- async function readAll () {
- console.log("方法头部");
- const result1 = await readFileByPath('./files/1.txt')
- console.log(result1)
- const result2 = await readFileByPath('./files/2.txt')
- console.log(result2)
- const result3 = await readFileByPath('./files/3.txt')
- console.log(result3)
- console.log("方法尾部");
- }
- console.log("结束");
- readAll()
运行顺序:
原因:
ES系列之Promise async 和 await的更多相关文章
- promise, async和await
最开始实现异步的方法:回调函数 method1(function(err, result) { if (err) { throw err; } method2(function(err, result ...
- promise以及async、await学习总结
Promise/async.await帮我们解决了什么 它给我们提供了一种新的异步编程解决方案,同时避免了困扰已久的回调地狱 // 异步的处理可能会产生这样的回调地狱(第二个异步操作和第一个异步的结果 ...
- ES 7 async/await Promise
如何添加SSL证书实现https请求 https://blog.csdn.net/lupengfei1009/article/details/76828190/ ES 7 async/awai ...
- C#基础系列——异步编程初探:async和await
前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法.确实,没有异步的多线程是单调的.乏味的,async和await是出现在C#5.0之后,它的出现给了 ...
- Promise,Async,await简介
Promise 对象 转载:http://wiki.jikexueyuan.com/project/es6/promise.html 基本用法 ES6 原生提供了 Promise 对象.所谓 Prom ...
- promise async await使用
1.Promise (名字含义:promise为承诺,表示其他手段无法改变) Promise 对象代表一个异步操作,其不受外界影响,有三种状态: Pending(进行中.未完成的) Resolved( ...
- 详解promise、async和await的执行顺序
1.题目和答案 一道题题目:下面这段promise.async和await代码,请问控制台打印的顺序? async function async1(){ console.log('async1 sta ...
- Promise、async、await在Egret的简单应用
Egret Engnie 5.1.10 Egret Wing 4.1.5 一.Promise.async.await相关知识 Promise介绍 阮一峰 async函数 阮一峰 具体和详细的说明用法可 ...
- 反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) C#中缓存的使用 C#操作redis WPF 控件库——可拖动选项卡的TabControl 【Bootstrap系列】详解Bootstrap-table AutoFac event 和delegate的分别 常见的异步方式async 和 await C# Task用法 c#源码的执行过程
反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) 背景介绍: 为了平衡社区成员的贡献和索取,一起帮引入了帮帮币.当用户积分(帮帮点)达到一定数额之后,就会“掉落”一定数量的“帮帮 ...
随机推荐
- 【转】Spark Streaming 实时计算在甜橙金融监控系统中的应用及优化
系统架构介绍 整个实时监控系统的架构是先由 Flume 收集服务器产生的日志 Log 和前端埋点数据, 然后实时把这些信息发送到 Kafka 分布式发布订阅消息系统,接着由 Spark Streami ...
- 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. ...
- jdbc oracle 连接串
jdbc.url配置为: jdbc:oracle:thin:@xxx.xx.xx.xx:1521:orclpdb 报错: java.sql.SQLException: Listenerrefused ...
- POJ1723 SOLDIERS 兄弟连
SOLDIERS 有一个性质:在一个长为n的序列a中找一个数 \(a_k\) 使得 \(\sum\limits_{i=1}^n abs(a_i-a_k)\) 最小,则 \(a_k\) 是a的中位数. ...
- HDU - 6181 Two Paths(次短路)
题意:求次短路. 分析:关键是情况讨论. LL tmpd = x.d + e.dist; 以下情况对应的更新结果 1.tmpd(2) < 最短路(3) < 次短路(4)-------> ...
- .Net 经典案例
1.捕捉一只小可爱 using System; using System.Collections.Generic; using System.Linq; using System.Text; usin ...
- 151-PHP nl2br函数(二)
<?php $str="h\nt\nm\nl"; //定义一个多处换行的字串 echo "未处理前的输出形式:<br />{$str}"; $ ...
- 112-PHP类变量之间的赋值标识为同一个对象(二)
<?php class mao{ //定义猫类 public $age=0; //定义多个属性并初始化 public $weight=50; public $color='white'; } $ ...
- C# winform中ListView用法
this.listView1.GridLines = true; //显示表格线 this.listView1.View = View.Details;//显示表格细节 this.listView1. ...
- 箭头函数this
箭头函数的this值是由包含它的函数(非箭头函数)来决定的,与包含的函数的this指向一致,如果包裹它的不是函数(直到找到最外层)则this指向全局对象 并且箭头函数的this是固定的,由定义它时所在 ...