异步回调的泥潭

异步回调是最直接的异步结果处理模式,将一个回调函数callback扔进异步处理函数中,当异步处理获得结果之后再调用这个回调函数就可以继续之后的处理,但是如果这个callback又是一个异步调用呢?众所周知的,在JavaScript中异步回调的层层嵌套几乎是反人性的,代码编写、修改和阅读对我等有代码洁癖的人而言是一种煎熬,这便是异步回调泥潭了。

今天对于处理异步调用已经有了很多成熟的方案,在我看来这些方案都无外乎在解决一个问题:“如何能看似顺序地传递异步调用的结果?”,本文要说的Promise就是ES6原生提供的一个解决方案。

在对Promise进行叙述之前,依旧引用阮大的《ECMAScript 6入门》一书中的Promise章节便于大家更严谨和全面的学习和参考。

Promise

承诺,即对未来的许诺,如果诺言实现,然后then)就如何如何……Promise极其生动的讲述了一个言出必行的故事。

new Promise(function(resolve, reject){//开始实现承诺........if(承诺兑现时) {resolve(dollars);  //兑现承诺的结果是得到'一大笔美金'} else {reject('绝交');  //没兑现承诺就绝交}}).then(function(dollars){  //然后有钱了,买房买车娶妻生子let d1 = buyHouse(dollars); //把每次消费剩余的钱传给下一个函数let d2 = buyCar(d1);let d3 = marry(d2);makeBaby(d3);}).catch(function(result){//然后如果绝交了,还是继续吃土//继续吃土});console.log('故事开始....');

看过上面的这个俗不可耐的故事之后需要理解几件事情:

  1. 言出必行:一个Promise构造出来之后,构造时传入的异步函数就立即执行;*
    注:因大凡使用promise都是在异步调用场景,下文所说的异步函数都是指构造promise时传入的函数*

  2. Promise实例内部维护了一个状态机,状态变化只可能是pendingresolved或者pendingrejected;

    • 执行resolvepending变化到resolved

    • 执行rejectpending变化到rejected

    • 抛出错误:pending变化到rejected

  3. then的第一个回调函数只会在发生了resolve之后执行,本质上是在Promise到达resolved状态执行;

  4. then的第二个回调函数或者catch的回调函数会在发生reject之后或者异步函数执行抛出错误时执行,本质上是在promise到达rejected状态时执行;

  5. 异步函数执行得到结果可以通过resolve或者reject将结果传出;

    • 调用resolve传入的值会作为then第一个回调函数的入参

    • 调用reject传入的值作为then第二个回调函数或者catch的回调函数的入参

    • 如果异步函数抛出了异常,异常会作为then第二个回调函数或者catch的回调函数的入参

  6. '故事开始....'会先输出,而不是等到then的回调函数执行完毕才输出,说明传入then的回调函数是异步执行,同理catch也是一样;

异步函数调用链

thencatch都是Promise的实例方法,都返回一个新的Promise,因此可以轻而易举地实现链式编程,比如上面的例子中“把每次消费剩余的钱”传给下一个函数可以改写成这样:

....//前面省略.then(function(dollars){  return buyHouse(dollars);}).then(function(d1){return buyCar(d1);}).then(function(d2){return marry(d2);}).then(function(d3){return makeBaby(d3);}).catch(function(result){//继续吃土});

看到这里你可能认为前一个then回调函数的返回值是后一个then的回调函数的入参,但这是不准确的,因为当then回调函数返回的是个Promise对象时,这个Promise对象到终态时后一个then才会执行,并且该Promise对象执行resolve时的入参才是后一个then的回调函数入参;

此时有必要对Promise的一个类方法resolve做以下说明,它的特性两句话:

  1. 如果传入的是个Promise对象,则直接返回这个Promise

  2. 如果是其他任何一个值(包括Error对象和undefined)则直接转换为一个resolved状态的Promise对象;

比如说下面的代码:

//以下的p1和p2逻辑上等同let p1 = Promise.resolve(1);let p2 = new Promise(function(resolve, reject) {resolve(1);});//以下的p3和p4等同let p3 = new Promise(function(r, j) {});let p4 = Promise.resolve(p3);console.log(p3 == p4); //trueconsole.log(p3 === p4); //true//以下三者逻辑上等同Promise.resolve().then(function(dollars) {return 1 + 1;}).then(function(v) {console.log(v);});Promise.resolve().then(function(dollars) {return new Promise(function(r, j) { r(1 + 1) });}).then(function(v) {console.log(v);});Promise.resolve().then(function(dollars) {return Promise.resolve(1 + 1);}).then(function(v) {console.log(v);});

我们可以利用Promise异步执行结果传出的机制和then的链式调用,将层层嵌套的函数调用变为通过then顺序连接的链式调用
从写法和形式上看是不是人性很多呢?

通过Promise实现的链式异步函数调用,以斐波那契数列举例如下:


//一个异步的斐波那契计算
function fibonacci(v) { return new Promise(function(resolve, reject) {  //每一个异步调用都返回了一个PromisesetTimeout(function() {console.log(`${v.a}`);[v.a, v.b] = [v.b, v.a + v.b];resolve(v);}, 500);});
}//以下两者逻辑等同,每个then都等待上一个promise的结果形成一条链。// fibonacci({ a: 0, b: 1 })
//     .then(fibonacci)
//     .then(fibonacci)
//     .then(fibonacci)
//     .then(fibonacci)
//     .then(fibonacci)
//     .then(fibonacci);Promise.resolve().then(() => fibonacci({ a: 0, b: 1 })).then(fibonacci).then(fibonacci).then(fibonacci).then(fibonacci).then(fibonacci).then(fibonacci);

ES6 异步编程之二:Promise相关推荐

  1. 异步编程之Promise(2):探究原理

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  2. python3 sleep 并发_python异步编程之asyncio(百万并发)

    点击上方蓝字关注我们 目录 [python 异步编程之 asyncio(百万并发)] 一.asyncio 二.aiohttp 前言:python 由于 GIL(全局锁)的存在,不能发挥多核的优势,其性 ...

  3. pythonasyncio并发编程实战_python异步编程之asyncio(百万并发)

    [python异步编程之asyncio(百万并发)] 前言:python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病.然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上 ...

  4. Python网络编程之二:网络编程基础

    Python网络编程之二:网络编程基础 一.基础概念 1.两个地址 1.1.mac地址 mac地址:直译为媒体存取控制位址,也称为局域网地址.MAC位址.以太网地址或物理地址,它是一个用来确认网络设备 ...

  5. 前端异步编程之Promise和async的用法

    传统的异步解决方案采用回调函数和事件监听的方式,而这里主要记录两种异步编程的新方案: ES6的新语法Promise ES2017引入的async函数 Generator函数(略) Promise的含义 ...

  6. JS异步编程之Generator

    前言 ES6 中提出一个叫生成器(Generator)的概念,执行生成器函数,会返回迭代器对象(Iterator),这个迭代器对象可以遍历函数内部的每一个状态. function* helloWorl ...

  7. JS异步编程之callback

    为什么 JS 是单线程? 众所周知,Javascript 语言的执行环境是"单线程"(single thread). 所谓"单线程",就是指一次只能完成一件任务 ...

  8. python异步asy_Python 异步编程之asyncio【转载】

    一.协程的认识 协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术. 简而言之,其实就是通过一个线程实现代码块相互切换执行.例如:deffunc1():print(1) . ...

  9. Node.js 异步编程之 Callback介绍

    原文:http://www.jb51.net/article/63070.htm ------------------------------------- Node.js 基于 JavaScript ...

最新文章

  1. h5有缓存css,taro H5配置 cdn css js 缓存 hash 配置
  2. 电脑主板线路连接图解_电工速学手册:306页现场电工全能图解,实用技术精选大合集!...
  3. Leetcode 202. 快乐数 解题思路及C++实现
  4. python人脸识别pdf百度云_调用百度云接口实现人脸识别与文字识别
  5. 3DSlicer20:GUI Structure
  6. 54 Django 模型层(1) 单表查询
  7. LeetCode 148. 排序链表(归并排序、快速排序)
  8. OPPO Reno3系列旗舰官宣:骁龙765G+正反双曲面设计
  9. inside-the-linux-kernel-full
  10. Ciena 在MEF16发布开放分布式NFV方案
  11. Linux kernel路由机制分析(下)
  12. c语言是非结构化程序语言_1、C语言是一种结构化程序设计语言
  13. matlab cnn 实例,Deep Learning学习 之 CNN代码解析(MATLAB)(示例代码)
  14. 工程施工工地进度监控带天气经纬度相机(监理日志不再难写)
  15. python数据分析学习和建模的个人笔记(一)
  16. 获取iOS设备唯一标示
  17. PAT 1082 射击比赛 python
  18. c# WindowsForm上使用Panel制作画板的一些小功能
  19. Spring Websocket 使用笔记
  20. 罗克韦尔PLC编程软件ControlLogix平台

热门文章

  1. matlab中的@函数
  2. C++目录遍历:使用第三方库boost.filesystem等
  3. 一文读懂RPA与BPM的区别和联系
  4. VMware-viewagent-direct-connection安装
  5. Linux macos 常用终端操作
  6. hdu 5384 Danganronpa(字典树)
  7. Java程序员之完美代码
  8. MySQL---数据库从入门走向大神系列(十五)-Apache的DBUtils框架使用
  9. .NET Core系列 : 2 、project.json 这葫芦里卖的什么药
  10. ios-UIButton-常用方法