资讯专栏INFORMATION COLUMN

ES6 异步编程之二:Promise

Gilbertat / 2815人阅读

摘要:今天对于处理异步调用已经有了很多成熟的方案,在我看来这些方案都无外乎在解决一个问题如何能看似顺序地传递异步调用的结果,本文要说的就是原生提供的一个解决方案。在对进行叙述之前,依旧引用阮大的入门一书中的章节便于大家更严谨和全面的学习和参考。

异步回调的泥潭

异步回调是最直接的异步结果处理模式,将一个回调函数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("故事开始....");

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

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

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

执行resolvepending变化到resolved

执行rejectpending变化到rejected

抛出错误:pending变化到rejected

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

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

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

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

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

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

"故事开始...."会先输出,而不是等到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做以下说明,它的特性两句话:

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

如果是其他任何一个值(包括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); //true
    console.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) {  //每一个异步调用都返回了一个Promise
        setTimeout(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);

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86780.html

相关文章

  • ECMAScript 6新特性印象之二:面对对象和模块化

    摘要:本文参考了以下文章之前的文章新特性印象之一新语法面对对象关键字看上面例子就能明白。定义类的,配合创建新对象。继承非构造器对象的原型是。错误检查继承的目标一定要是个对象或者。的构造器是可改写,但不可枚举。引入了一个标签,负责载入模块。 本文参考了以下文章/PPT: Use ECMAScript 6 today Ecmascript 6 Whats next for Javascrip...

    darryrzhong 评论0 收藏0
  • ES6-7

    摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...

    mudiyouyou 评论0 收藏0
  • JavaScript 异步

    摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。写一个符合规范并可配合使用的写一个符合规范并可配合使用的理解的工作原理采用回调函数来处理异步编程。 JavaScript怎么使用循环代替(异步)递归 问题描述 在开发过程中,遇到一个需求:在系统初始化时通过http获取一个第三方服务器端的列表,第三方服务器提供了一个接口,可通过...

    tuniutech 评论0 收藏0
  • 谈谈ES6前后的异步编程

    摘要:回调函数这是异步编程最基本的方法。对象对象是工作组提出的一种规范,目的是为异步编程提供统一接口。诞生后,出现了函数,它将异步编程带入了一个全新的阶段。 更多详情点击http://blog.zhangbing.club/Ja... Javascript 语言的执行环境是单线程的,如果没有异步编程,根本没法用,非卡死不可。 为了解决这个问题,Javascript语言将任务的执行模式分成两种...

    fizz 评论0 收藏0
  • es6 - Promise

    摘要:所谓异步编程中的异步是相对于同步的概念的。是一系列异步编程规范的统称。如果中的回调函数返回一个值,那么返回的将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。参考介绍基础篇深入理解与异步编程。 es6 promise与异步编程 对于一些还不具备大量编程经验的朋友来说,promise可能是es6比较难以掌握的点。首先是很多名词,比如Promises,es6 Promise,...

    wemallshop 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<