异步promise、Async/await介绍

promise基本介绍

  • Promise 是异步编程的一种解决方案,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,有以下特点:
  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。
  3. 最要用于解决异步的层层嵌套,防止嵌套黑洞,是代码结构更简洁、提高可读性

promise 应用

  1. 最基本的用法
const promise = new Promise(function(resolve, reject) {// ... some codeif (/* 异步操作成功 */){resolve(value);} else {reject(error);}
});
复制代码
  1. promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,它们是两个函数。
  2. resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”,
  3. reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected)
  4. 实例生成后可以这样使用
promise.then(function(value) {// success
}, function(error) {// failure
});
复制代码

2、延迟操作

function delay(time) {return new Promise(function(resolve, reject){setTimeout(resolve, time);});
}delay(1000)
.then(function(){console.log("after 1000ms");return delay(2000);
})
.then(function(){console.log("after another 2000ms");
})
.then(function(){console.log("step 4 (next Job)");return delay(5000);
})
// ...
复制代码
  1. 图片加载
function loadImageAsync(url) {return new Promise(function(resolve, reject) {const image = new Image();image.onload = function() {resolve(image);};image.onerror = function() {reject(new Error('Could not load image at ' + url));};image.src = url;});
}
复制代码
  1. 简易版封装数据获取
const getJSON = function(url) {const promise = new Promise(function(resolve, reject){const handler = function() {if (this.readyState !== 4) {return;}if (this.status === 200) {resolve(this.response);} else {reject(new Error(this.statusText));}};const client = new XMLHttpRequest();client.open("GET", url);client.onreadystatechange = handler;client.responseType = "json";client.setRequestHeader("Accept", "application/json");client.send();});return promise;
};getJSON("/posts.json").then(function(json) {console.log('Contents: ' + json);
}, function(error) {console.error('出错了', error);
});
复制代码
  • 示例5:每10秒检测一次用户是否登录
function onUserLoggedIn(id) {return ajax(`user/${id}`).then(user => {if (user.state === 'logged_in') {return;}return new Promise(resolve => {return setTimeout(resolve, 10 * 1000));}).then(() => {return onUserLoggedIn(id);})});
}复制代码
  • 示例5改进:业务代码与promise层的代码混合在一起,可读性差,继续改进
function delay(time) {return new Promise(resolve => {return setTimeout(resolve, time));});
}function until(conditionFn, delayTime = 1000) {return Promise.resolve().then(() => {return conditionFn();}).then(result => {if (!result) {return delay(delayTime).then(() => {return until(conditionFn, delayTime);});}});
}
复制代码
  • 示例5箭头函数改进
let delay = time =>new Promise(resolve =>setTimeout(resolve, time));
let until = (cond, time) =>cond().then(result =>result || delay(time).then(() =>until(cond, time)));
复制代码
  • 使用示例5
// 写法一
function onUserLoggedIn(id) {return until(() => {return ajax(`user/${id}`).then(user => {return user.state === 'logged_in';});}, 10 * 1000);
}// 写法二
async function onUserLoggedIn(id) {return await until(async () => {let user = await ajax(`user/${id}`);return user.state === 'logged_in';}, 10 * 1000);
}
复制代码
  1. callback回调异步转换
function callbackToPromise(method, ...args) {return new Promise(function(resolve, reject) {return method(...args, function(err, result) {return err ? reject(err) : resolve(result);});});
}async function getFirstUser() {let users = await callbackToPromise(getUsers);return users[0].name;
}
复制代码
  • 异常处理
var p = Promise.resolve(374);p.then(function fulfilled(msg){// numbers don't have string functions,// so will throw an errorconsole.log(msg.toLowerCase());
})
.done(null, function() {// If an exception is caused here, it will be thrown globally
});
复制代码
  • promise 实现的参考

Async/await

Async/await 基本介绍

  • async函数是对Generator 函数的改进

基本用法

  • async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
  • async函数内部return语句返回的值,会成为then方法回调函数的参数。
async function getStockPriceByName(name) {const symbol = await getStockSymbol(name);const stockPrice = await getStockPrice(symbol);return stockPrice;
}getStockPriceByName('goog').then(function (result) {console.log(result);
});
复制代码
  • async 函数有多种使用形式。
// 函数声明
async function foo() {}// 函数表达式
const foo = async function () {};// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)// Class 的方法
class Storage {constructor() {this.cachePromise = caches.open('avatars');}async getAvatar(name) {const cache = await this.cachePromise;return cache.match(`/avatars/${name}.jpg`);}
}const storage = new Storage();
storage.getAvatar('jake').then(…);// 箭头函数
const foo = async () => {};
复制代码
  • await 命令
  • await命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve的 Promise 对象。
async function f() {return await 123;
}f().then(v => console.log(v))
复制代码
  • 只要一个await语句后面的 Promise 变为reject,那么整个async函数都会中断执行
async function f() {await Promise.reject('出错了');await Promise.resolve('hello world'); // 不会执行
}
复制代码

使用注意点

  • 陷阱:在使用异步方式时,忘记使用await
async function getFirstUser() {try {let users = await getUsers();return users[0].name;} catch (err) {return {name: 'default user'};}
}// 注意添加 await
let user = await getFirstUser();
复制代码
  • 陷阱:不存在继发关系同时使用多个await
let foo = await getFoo();
let bar = await getBar();// 应改为如下所示的方法,并行运行等待结果,
//性能上有优势
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);});
}// 改进这是继发执行
async function dbFuc(db) {let docs = [{}, {}, {}];for (let doc of docs) {await db.post(doc);}
}// 并发执行可作如此修改,写法1
async function dbFuc(db) {let docs = [{}, {}, {}];let promises = docs.map((doc) => db.post(doc));let results = await Promise.all(promises);console.log(results);
}
// 写法2
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);
}
复制代码
  • 处理异常
myApp.registerEndpoint('GET', '/api/firstUser', async function(req, res) {try {let firstUser = await getFirstUser();res.json(firstUser)} catch (err) {console.error(err);res.status(500);}
});
复制代码

promise与Asyns联合使用,解决继发、并发同存在的问题

  • 示例1
  • 我想做一个披萨
  • 独立制作面团
  • 独立制作酱料
  • 在我们决定什么样的奶酪放进披萨时,我们希望能够品尝酱汁,使披萨味道最美味,
// 示例1
async function makePizza(sauceType = 'red') {let dough  = await makeDough();let sauce  = await makeSauce(sauceType);let cheese = await grateCheese(sauce.determineCheese());dough.add(sauce);dough.add(cheese);return dough;
}
复制代码
  • 示例1运行如下
|-------- dough --------> |-------- sauce --------> |-- cheese -->
制作面团--->制作酱料--->磨碎奶酪,这是最好的么?我们要等待面团完成后,才能尝酱的味道,有没有更快的方法呢?
复制代码
  • 示例改进
async function makePizza(sauceType = 'red') {let [ dough, sauce ] =await Promise.all([ makeDough(), makeSauce(sauceType) ]);let cheese = await grateCheese(sauce.determineCheese());dough.add(sauce);dough.add(cheese);return dough;
}
复制代码
  • 运行如下
|-------- dough -------->
|--- sauce --->           |-- cheese -->
- Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
- 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
- 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
- 虽然,douch与sauce并行,相较于1会更快一点磨碎奶酪,从Promise.all方法运行机制来看,但仍然要等待面、酱料制作好了,才能磨碎奶酪,那么有更好的办法么?
复制代码
  • 示例3
function makePizza(sauceType = 'red') {let doughPromise  = makeDough();let saucePromise  = makeSauce(sauceType);let cheesePromise = saucePromise.then(sauce => {return grateCheese(sauce.determineCheese());});return Promise.all([ doughPromise, saucePromise, cheesePromise ]).then(([ dough, sauce, cheese ]) => {dough.add(sauce);dough.add(cheese);return dough;});
}// or 用asyns/await改写
async function makePizza(sauceType = 'red') {let doughPromise = makeDough();let saucePromise = makeSauce(sauceType);let sauce  = await saucePromise;let cheese = await grateCheese(sauce.determineCheese());let dough  = await doughPromise;dough.add(sauce);dough.add(cheese);return dough;
}
复制代码
  • 运行结果如下
|--------- dough --------->
|---- sauce ----> |-- cheese -->
从代码阅读来看,这不是最好的写法,继续改进
复制代码
  • 示例1 终版跟记忆函数的结合
function memoize(method) {let cache = {};return async function() {let args = JSON.stringify(arguments);cache[args] = cache[args] || method.apply(this, arguments);return cache[args];};
}async function makePizza(sauceType = 'red') {let prepareDough  = memoize(async () => makeDough());let prepareSauce  = memoize(async () => makeSauce(sauceType));let prepareCheese = memoize(async () => {return grateCheese((await prepareSauce()).determineCheese());});let [ dough, sauce, cheese ] = await Promise.all([prepareDough(), prepareSauce(), prepareCheese()]);dough.add(sauce);dough.add(cheese);return dough;
}
复制代码
  • 示例二: 制作汤
async function getSoupRecipe(<soupType>)
async function hireSoupChef(<soupRecipe:requiredSkills>)
async function buySoupPan()
async function makeSoup(<soupChef>, <soupRecipe>, <soupPan>)
- 提供一些代码制作我最喜欢的汤
- 制作汤需要配方、厨师、锅
- 上面的代码对于一些方法来说是一个松散的界面,它可以让你得到你需要的东西来制作汤
复制代码
  • 基本方法
async function getSoupRecipe(soupType) {return await http.get(`/api/soup/${soupType}`);
}async function buySoupPan() {return await http.get(`/api/soupPan`);
}async function hireSoupChef(requiredSkills) {return await http.post(`/api/soupChef/hire`, {requiredSkills: requiredSkills});
}async function makeSoup(soupChef, soupRecipe, soupPan) {return await http.post(`api/makeSoup`, {soupRecipe, soupPan, soupChef});
}
复制代码
  • 示例2:暴力写法
async function makeSoupFromType(soupType) {let soupRecipe = await getSoupRecipe(soupType);let soupPan    = await buySoupPan();let soupChef   = await hireSoupChef(soupRecipe.requiredSkills);return await makeSoup(soupChef, soupRecipe, soupPan);
}
复制代码
  • 代码是可以工作的,但是是继发执行的,买锅和聘请厨师并没有前后关系,这就会导致效率的低下,煮汤的时间延长,会饿肚子的。如何改进成将有继发关系的让他一步一步执行,没有继发关系的,并发执行,以提升效率。

  • 示例2改进型

async function makeSoupFromType(soupType) {let [soupRecipe, soupPan] = await* [getSoupRecipe(soupType),buySoupPan()];let soupChef = await hireSoupChef(soupRecipe.requiredSkills);return await makeSoup(soupChef, soupRecipe, soupPan);
}
复制代码
  • await* 只是一种表示方法,在真实的代码中应该用Promise.all()
  • 这时代码发生了耦合,明确指出'这两种方法必须并行运行'和'此后必须运行,维护性较差
  • 聘请厨师还是需要有锅,不合逻辑。
  • 继续改进示例2
async function makeSoupFromType(soupType) {let soupRecipePromise = getSoupRecipe(soupType);async function hireSoupChefWithSoupRecipe(_soupRecipePromise) {let soupRecipe = await _soupRecipePromise;return await hireSoupChef(soupRecipe.requiredSkills);}let [ soupRecipe, soupPan, soupChef ] = await* [soupRecipePromise,buySoupPan(),hireSoupChefWithSoupRecipe(soupRecipePromise)]);return await makeSoup(soupChef, soupRecipe, soupPan);
}
复制代码
  • 汤料要获取两次,并提前缓存汤料

  • 不方便阅读理解,在以后的维护、改进过程中,如果在 getSoupRecipe(soupType)加上await又会变慢

  • 示例2:继续改进,看看缓存函数和asyns能发生什么化学反应

  • 考虑我们在上述方法中遇到的问题。我们需要得到一份汤配方,但我们不想两次拿取,所以我们必须自己手动缓存。

function memoize(method) {let cache = {};return async function() {let args = JSON.stringify(arguments);cache[args] = cache[args] || method.apply(this, arguments);return cache[args];};
}let getSoupRecipe = memoize(async function(soupType) {return await http.get(`/api/soup/${soupType}`);
});let buySoupPan = memoize(async function() {return await http.get(`/api/soupPan`);
});let hireSoupChef = memoize(async function(soupType) {let soupRecipe = await getSoupRecipe(soupType)return await http.post(`/api/soupChef/hire`, {requiredSkills: soupRecipe.requiredSkills});
});let makeSoup = memoize(async function(soupType) {let [ soupRecipe, soupPan, soupChef ] = await* [     getSoupRecipe(soupType), buySoupPan(), hireSoupChef(soupType)];return await http.post(`api/makeSoup`, {soupRecipe, soupPan, soupChef});
});
复制代码
  • memoize()把任何异步函数变成一个memoized函数 - 也就是说,如果它被调用了两次相同的参数,它将在第二次返回相同的缓存值。
  • memoize函数中不使用await - 我们实际缓存的内容是方法返回的诺言,而不是最终值。这意味着我们甚至不需要等待异步方法返回任何内容,然后再缓存它的未来值。

思考点

  • Async/await可以解决回调黑洞的问题,给开发、和层序的阅读性带来便捷。但是暴力、滥用Async/await同样也会带来性能问题,并发关系的被写成继发关系,就会大大影响性能。
  • 要做好异常的处理工作。

参考链接

  • 参考链接---eventloop-promise---asyns
  • 参考链接---promise1
  • 参考链接---promise2
  • 参考链接---记忆函数与异步并发的最好结合,看懂这篇文章,对异步并行就有了相当的理解
  • 参考链接-阮一峰-promise

异步promise、Async/await介绍相关推荐

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

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

  2. ES6箭头函数以及promise/async/await测试案例

    ES6箭头函数的运用 下面以一段代码解释 function one(){return 1 以上函数用箭头函数写步骤,参考下面代码 {}和里面的东西先删去 one = (里面写参数/无参数的里面为空)= ...

  3. [C#] 谈谈异步编程async await

    [C#] 谈谈异步编程async await 转载于:https://www.cnblogs.com/macT/p/9288112.html

  4. js callback promise async await 几种异步函数处理方式

    ***callback  这个是最常用的也是最简单的 ,比如在ajax网络请求中,返回请求完成返回的数据 回调函数就是把一个函数当成另一个函数的参数,可以传递函数内的局部变量,也可以异步完成一些操作, ...

  5. rust异步编程--理解并发/多线程/回调/异步/future/promise/async/await/tokio

    1. 异步编程简介 通常我们将消息通信分成同步和异步两种: 同步就是消息的发送方要等待消息返回才能继续处理其它事情 异步就是消息的发送方不需要等待消息返回就可以处理其它事情 很显然异步允许我们同时做更 ...

  6. angular2 学习笔记 ( Rxjs, Promise, Async/Await 的区别 )

    Promise 是 ES 6 Async/Await 是 ES 7 Rxjs 是一个 js 库 在使用 angular 时,你会经常看见这 3 个东西. 它们都和异步编程有关,有些情况下你会觉得用它们 ...

  7. async js 返回值_JS异步编程 | Async / Await / Generator 实现原理解析

    async/await实现 在多个回调依赖的场景中,尽管Promise通过链式调用取代了回调嵌套,但过多的链式调用可读性仍然不佳,流程控制也不方便,ES7 提出的async 函数,终于让 JS 对于异 ...

  8. ES2017 异步函数async/await

    ES2017标准已于2017年6月份正式定稿了,并广泛支持最新的特性:异步函数.如果你曾经被异步 JavaScript 的逻辑困扰,这么新函数正是为你设计的. 异步函数或多或少会让你编写一些顺序的 J ...

  9. 【面试题】前端人70%以上 不了解的promise/async await

    前言   今天给大家分享promise,笔者将从早期的异步代码的困境.promise出现解决了什么问题.异步回调地狱的终极方案并且实现async await的核心语法,其实async/await只是g ...

  10. JS实现sleep,普通版+promise+async/await

    1-1 普通版 function sleep() {var start = new Date()while(new Date() - start <= sleepTime) {}var t1 = ...

最新文章

  1. python代码块-python代码块
  2. GDCM:独特的uid测试程序
  3. golang map源码分析
  4. P6640-[BJOI2020]封印【SAM,二分】
  5. java 单元测试技巧_其他一些单元测试技巧
  6. Python自动化开发01
  7. ZZULIOJ 1127: 矩阵乘积
  8. C语言头文件一般以什么名称结尾,c语言书写规范.doc
  9. LeetCode每日一题——无重复字符的最长子串
  10. CV笔记5:图像分割之最大类间方差法、自适应阈值分割(基于python-opencv实现)
  11. 各大网站收录入口| 各大搜索引擎提交 | 搜索引擎提交地址
  12. 蓝牙定位技术原理--蓝牙人员定位--蓝牙定位--新导智能
  13. 实时展示摄像头内容(go server + electron-vue client)
  14. 关于破解ewebeditor V10.9 编辑器的问题
  15. java单词大全_编程常用英语单词大全
  16. linux各文件夹作用
  17. MySQL 中的boolean/bool/tinyint(1)表示布尔类型
  18. 阿里云服务器搭建JAVA环境详解(jdk+mysql+tomcat)
  19. R语言将文件写入CSV,并读取
  20. aps审核计算机笔试试题,APS审核—计算机英语审核复习--自我介绍

热门文章

  1. perl:非贪婪的数量词
  2. asp.net回调javascript
  3. wangeditor react中使用
  4. uni-app开发开发h5,小程序,app,注意事项
  5. php根据经纬度查询附近工人,并算出距离(tp3.2)
  6. logback-spring.xml文件配置
  7. ES6学习笔记一(var let const三者区别)
  8. 页面右下角弹广告案例
  9. 阿里云 oss 图片在 img 中访问失败,浏览器中正常访问
  10. 数据库操作语句类型(DQL、DML、DDL、DCL)简介