资讯专栏INFORMATION COLUMN

JavaScript Promise

Tamic / 2052人阅读

摘要:前言假如需要向后端发送一个请求,并对返回的数据进行操作,可能我们第一时间想到的是回调函数。如果值本身就是一个对象,则替代默认的对象作为返回值如果值为其他值,则将这个值作为返回的的函数的参数值。

前言

假如需要向后端发送一个请求,并对返回的数据进行操作,可能我们第一时间想到的是回调函数。但如果接着又需要执行第二个、第三个...第n个异步操作,那么回调函数就会一层层的嵌套,严重影响了代码可读性和可维护性。

Promise就是解决这个问题的方案,Promise主要做的事情是把回调函数的嵌套逻辑替换成了符合正常人思维习惯的线性逻辑,本文主要介绍Promise的基本用法、API、链式操作、异常处理以及利用promise对数组进行异步操作的方法。

一、杂说

Promise是从DOM中的Futures引入javascript的,理由大概是出现了像NodeJs这样独立于浏览器之外的JavaScript运行环境。在Promise正式被实现之前,部分JS库,像Q、when、WinJS、RSVP.js、jQuery都根据Promises/A+标准分别实现了略有差异的”类Promise“对象。如果你的项目中用到了这些库,不用担心,标准的Promise对象提供了将这些”类Promise“对象转换为标准Promise对象的方法(后文会提到)。关于Promise的兼容性,参考Can I Use。

Promise有三种状态:pending、resolved、rejected,状态之间的转换只能从pending到resolved或rejected,并且状态一旦转换就再也无法改变;

Promise的API:

Promise的构造器接受一个函数,这个函数接受两个参数:resolved,rejected。

promise.then(onResolved, onRejected), 不做赘述;

promise.catch(onRejected), promise.then(undefined, onRejected)的语法糖。

Promise.resolve(argument),返回一个Promise对象,具体取决于它接受的参数类型。

参数为一个Promise对象,直接返回这个对象;

参数为一个“类promise”对象,将其转化成真正的Promise对象并返回;

参数为其他值,返回一个以参数值作为其resolved函数参数的Promise对象;

Promise.reject(obj), 返回一个以参数值(Error的实例)作为其reject函数参数的Promise对象;

Promise.all(array), 参数值为Promise数组(也可以包含"类Promise"对象),对数组的每一项调用Promise.resolve(),全部成功则resolved并返回返回值的数组,否则返回第一个rejected的error对象;

Promise.race(array), 返回数组中最先resolved或者rejected的那个Promise对象的返回值或者error对象。

二、基本用法

Promise是一个JavaScript对象,它执行在未来的某个时刻才知道结果的操作并返回得到的值或者失败的信息。

// Promise is something like this.  
var promise = new Promise(function(resolved, rejected) {
    doSomethingAsync();
 
    if (success) {
        resolved();
    } else {
        rejected();
    }
})

//How to use a promise. First arg is resolved, second is rejected
promise.then(function(res) {
    console.log(res);
}, function(err) {
    alert(err);
})
三、链式调用

如果仅有一个Promise对象的话,情况较为简单,即在Promise对象被定义时异步操作就开始执行,我们关心的并不是它什么时候执行完毕,而是要在它执行完或者返回错误后对结果进行处理。但当多个Promise要按照一定的顺序执行时,事情就变得复杂起来了。

function fetchSomething() {
    return new Promise(function(resolved) {
        if (success) {
            resolved(res);
        }
    });
}
fetchSomething().then(function(res) {
    console.log(res);
    return fetchSomething();
}).then(function(res) {
    console.log("duplicate res");
    return "done";
}).then(function(tip) {
    console.log(tip);
})

then函数始终返回一个promise对象,后续的then要等待返回的promise resolve后才能执行,这样就实现了线性逻辑的链式调用。而返回的promise取决于then函数本身return的值。如果return值本身就是一个promise对象,则替代默认的promise对象作为返回值;如果return值为其他值,则将这个值作为返回的promise的resolve函数的参数值。

四、异常处理

从上面的代码可以看出,then函数接受两个参数:resolved、rejected。上面没写rejected是因为rejected函数是可选的,当然也可以在then之后写catch,.catch(rejected)本质上是.then(undefined, rejected)的语法糖。
这两种方式是有区别的,.then(resolved, rejected)只能捕获之前的promise的异常,而写在其后的.catch(undefined, rejected)还可以捕获其resolved函数产生的异常。另外只要Promise链中有一个promise对象抛出异常,其后所有的resolved都被跳过,直到这个异常被rejected或者catch处理。

五、排序

当需要用数组的数据执行异步操作,因为数组的遍历方法forEach、map等都是同步的,所以结果的顺序就取决于异步操作完成的顺序,如果对顺序有要求,这样就不尽人意。

// 假设fetchID返回一个Promise对象
names.forEach(function(name) {
    fetchID(name).then(function(id) {
        renderInfo(id);
    })
})

这个时候就需要利用then()来制定顺序:

names.reduce(function(sequence, name) {
    return sequence.then(function() {
        return fetchID(name);
    }).then(function(id) {
        renderID(id);
    })
}, Promise.then())

因为此时先遍历的name处理的结果将作为后面的sequence,构成了链式关系,就避免了下载速度决定顺序的问题。但仍然可以优化:因为此时的ID是获取一个,render一个的。如果能够先获取所有的ID再逐条渲染的话,性能会更好。

Promise.all(names.map(fetchID))
       .then(function(IDs) {
           IDS.forEach(function(id) {
               renderID(id);    //同步
           })
       })

参考文章:
JavaScript Promises: an Introduction (自备梯子)
Master the JavaScript Interview: What is a Promise? (自备梯子)

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

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

相关文章

  • JavaScript引擎是如何工作的?从调用栈到Promise你需要知道的一切

    摘要:最受欢迎的引擎是,在和中使用,用于,以及所使用的。怎么处理每个引擎都有一个基本组件,称为调用栈。也就是说,如果有其他函数等待执行,函数是不能离开调用栈的。每个异步函数在被送入调用栈之前必须通过回调队列。例如方法是在中传递的回调函数。   翻译:疯狂的技术宅 原文:www.valentinog.com/blog/engine… 从Call Stack,Global Me...

    zzbo 评论0 收藏0
  • JavaScript 异步

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

    tuniutech 评论0 收藏0
  • JavaScript引擎是如何工作的?从调用栈到Promise你需要知道的一切

    摘要:最受欢迎的引擎是,在和中使用,用于,以及所使用的。单线程的我们说是单线程的,因为有一个调用栈处理我们的函数。也就是说,如果有其他函数等待执行,函数是不能离开调用栈的。每个异步函数在被送入调用栈之前必须通过回调队列。 翻译:疯狂的技术宅原文:https://www.valentinog.com/bl... 本文首发微信公众号:前端先锋欢迎关注,每天都给你推送新鲜的前端技术文章 sh...

    Simon_Zhou 评论0 收藏0
  • ES6-7

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

    mudiyouyou 评论0 收藏0
  • 异步 JavaScriptPromise

    摘要:为这些回调函数分别命名并分离存放可以在形式上减少嵌套,使代码清晰,但仍然不能解决问题。如果在一个结束成功或失败,同前面的说明后,添加针对成功或失败的回调,则回调函数会立即执行。 异步? 我在很多地方都看到过异步(Asynchronous)这个词,但在我还不是很理解这个概念的时候,却发现自己常常会被当做已经很清楚(* ̄ロ ̄)。 如果你也有类似的情况,没关系,搜索一下这个词,就可以得到大致...

    livem 评论0 收藏0
  • javascript异步之Promise.all()、Promise.race()、Promise.

    摘要:的执行与状态无关当得到状态不论成功或失败后就会执行,原文链接参考链接对象 同期异步系列文章推荐谈一谈javascript异步javascript异步中的回调javascript异步与promisejavascript异步之Promise.resolve()、Promise.reject()javascript异步之Promise then和catchjavascript异步之async...

    clasnake 评论0 收藏0

发表评论

0条评论

Tamic

|高级讲师

TA的文章

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