通过一道笔试题浅谈javascript中的promise对象

因为前几天做了一个promise对象捕获错误的面试题目,所以这几天又重温了一下promise对象。现在借这道题来分享下一些很基础的知识点。

下面是一个面试题目,三个promise对象捕获错误的例子,返回结果有什么不同。

//使用throw添加错误事件
var p = new Promise(function(resolve, reject) {
    resolve("ok");
    throw new Error(‘error0‘);
    //setTimeout(function() { throw new Error(‘error1‘) }, 0);
});
p.then(function(value){
    console.log(value)
 })
 .catch(funcrion(err){
    console.log(err)
    });
process.on(‘unhandledRejection‘, function (err, p) { console.error(‘catch exception:‘,err.stack) });
//设置定时器来抛出错误事件
var p = new Promise(function(resolve, reject) {
    resolve("ok");
    //throw new Error(‘error0‘);
    setTimeout(function() { throw new Error(‘error1‘) }, 0);
});
p.then(function(value){
    console.log(value)  })
 .catch(funcrion(err){
    console.log(err)
    });
process.on(‘unhandledRejection‘, function (err, p) { console.error(‘catch exception:‘,err.stack) });
//同时添加错误事件
var p = new Promise(function(resolve, reject) {
    resolve("ok");
    throw new Error(‘error0‘);
    setTimeout(function() { throw new Error(‘error1‘) }, 0);
});
p.then(function(value){
    console.log(value)
 })
 .catch(funcrion(err){
    console.log(err)
    });
process.on(‘unhandledRejection‘, function (err, p) { console.error(‘catch exception:‘,err.stack) });

先把问题放在这里,如果一眼能看出结果的大大们就不用再往下面读了。。

大概在很早以前就有了解过javascript中实现异步编程的四种方式。分别是1.回调函数 2.事件监听 3.发布、订阅事件 4.promise对象

前三种我们可以说是屡见不鲜了,回调函数,事件监听这算是js的“灵魂”了。。发布、订阅事件也是比较常见的。今天我们就来浅显学习下promise对象,以及它能实现异步编程的原理,最后是上面那个题目的答案以及我个人的一些理解。

一、什么是promise对象,它能干什么?

Promises对象是在CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。现已在ECMAScript2015(ES6)中实现。

Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算.。一个Promise对象代表着一个还未完成,但预期将来会完成的操作。

Promise 对象是一个返回值的代理,这个返回值在promise对象创建时未必已知。它允许你为异步操作的成功或失败指定处理方法。 这使得异步方法可以像同步方法那样返回值:异步方法会返回一个包含了原返回值的 promise 对象来替代原返回值。

Promise对象有以下几种状态:

pending: 表示一个初始状态, 非 fulfilled 或 rejected。

fulfilled: 成功的操作。

rejected: 失败的操作。

每一个异步任务都会返回一个Promise对象,该对象有一个then方法,允许指定回调函数。可以根据Promise对象的状态相应的去执行对应的回调函数。我们大概了解了promise存在的意义,下面我们具体去看一下该对象常用的几个API。

二、常用的API

1.Promise.prototype.then()

promise实例具有then方法,因此then方法是定义在原型对象promise.prototype上的。它的作用是为promise实例添加状态改变时的回调函数。

then()方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。then方法返回的是一个新的promise实例,非原来的那个promise实例,因此可以采用链式写法,then方法之后还可调用一个then方法。

var p=new Promise(function(resolve,eject){
    resolve("ok");
});
p.then(function(value){console.log(val)},
 function(err)(console.log(err))
);

then方法的第二个参数一般不推荐写。有以下两个原因:第一个原因,由于是链式操作,这个then方法之后还可能会有其他操作,如果此时把错误捕捉的函数放在后面方法前边的话,并且之后再无错误捕获方法,then之后的错误就会捕捉不到。第二个原因是在then方法里面,两个参数都是回调函数写了一大堆,这样结构看起来比较混乱。

所以下面就有了这个方法,一般写在链式写法的最后。这样就可以捕获到前面所有的错误。

2.Promise.prototype.catch()

这个方法是.then(null,rejection)的别名,这也能看出这个方法是专门只能用来捕获错误信息,用于指定发生错误时的回调函数。

但是使用这个这个方法的时候要注意一下几点:

(1)当promise状态已经变成resolved的时候,再抛出错误时是无效的。看下面的代码。

var promise=new promise(function(resolve,reject){
   resolve("ok");
   throw new Error("test");
});
promise.then(function(value){consloe.log(val); })
            .catch(function(error){console.log(err)});

promise状态在resolve("ok");之后就会把promise的状态变为resolved,之后抛出错误也不会把promise状态变为rejected,所以catch方法并不会捕获到错误。

Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况有任意一种发生,状态就相当于凝固了,不会再变了,会一直保持这个结果。

(2)尽量将catch方法写在链式操作的最后,原因上面都已经说过了,也正是捕获错误不推荐写then方法的原因之一。错误会一直冒泡到最后,catch放在最后会捕捉到所有错误。当catch设置的过早,并且之后在没有catch方法的话,那么这个catch之后发生的错误不会被捕获到。

(3)当没有使用catch方法指定错误处理函数的回调函数时,promise对象里面抛出的错误不会传递到外层的代码。

3.Promise.resolve()

这个方法的作用就是将现有的对象转化为Promise对象,进而可以执行这些方法。

Promise.resolve("foo");

//这就相当于下面这种写法

new Promise(function(resolve){
   resolve("foo");
});

4.Promise.all()

这个方法用于将多个promise实例,包装成一个新的promise实例。

var p=Promise.all([p1,p2,p3]);

p1,p2,p3都是promise对象的实例,如果不是的话,则会调用Promise.resolve()方法,将参数转化为Promise实例,之后再继续进行进一步的处理。

并且要注意一下两点:

(1)只有当p1,p2,p3状态都变为fulfilled之后,p的状态才会变为fulfilled。

(2)只要p1.p2,p3中有任意一个状态变为rejected,p的状态就会变为rejected。

三、实现异步编程的原理

大概原理就是正如它们所说Promise对象相当于是一个状态机,在其内部使用resolve方法,使其由初始状态变为成功时的fulfilled状态或者执行失败后的rejected状态。这时内部的工作就完成了,开始由外部监听其内部的状态的改变,调用then()方法(catch()方法相当于then内部的第二个参数方法)对应的状态调用对应的处理函数。这样就大概是Promise对象实现异步编程的原理。

四、开头所给问题的答案

我们可以看到三个实例外部函数的写法一模一样,不同的是Promise对象内部抛出错误所使用的方法。

第一个使用throw抛出错误,理应被外部的catch方法所捕获到。但是但是。。之前已经说过Promise对象状态一旦被改变之后就“凝固”了,一旦执行

resolve("ok");

状态被设定为fulfilled之后,再进行抛出错误处理,错误也不会被后续的catch方法捕获到。所以这里只会去执行then()方法里面的内容。即只会打印出 “ok”。

第二个通过定时器抛出一个错误。这里虽然状态已经变为fulfilled,但是定时器抛出的错误属于异步抛出的错误,无法被try catch捕获到,因此和Promise对象无关,所以错误可以正常的抛出来,所以这里的答案应该是先打印出“ok”,之后抛出process里面定义的错误。

第三个同时使用throw和定时器抛出错误。是不是里应当和第二个执行情况一样吗?这样想就错了。当执行throw之后,虽然错误未被外部函数捕获处理,但这也是个实实在在存在的错误啊,对于javascript来说,有错就不会继续往下面执行了。所以并不会执行到定时器抛出错误就停止了。因此这个问题的答案应该是 只打印出“ok”。

嗯,结束。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

时间: 2024-10-28 08:43:55

通过一道笔试题浅谈javascript中的promise对象的相关文章

浅谈javascript中的包装对象

javascript中的对象类型分为内置类型和对象类型,其中内置类型包括sting number boolean null undefined五种:对象类型包括Array Function regExp Date 等等,统称为Object类型.我们知道在一个对象中包含一系列属性名/属性值的集合,可以通过"."来访问对象的属性或方法,如: 1 window.onload=function(){//可执行代码} 但我们常常可以看到这样的代码: 1 var str="hello w

浅谈Javascript中面向对象之对象

一.对象 1.1创建对象 Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象.一个简单的对象创建: var People = { name : "eavan", age : 24, getName : function(){ alert(this.name); //eavan } } 使用的时候就可以用People.name,获取People这个对象的name属性,或者是People.getName()来得到People的name值.另一

浅谈JavaScript中的原型模式

在JavaScript中创建对象由很多种方式,如工厂模式.构造函数模式.原型模式等: <pre name="code" class="html">//工厂模式 function createPerson(name,age,job) { var o = new Object; o.name = name; o.age = age; o.job = job; o.sayName = function() { alert(this.name); } retur

浅谈javascript中的call、apply、bind

apply.call 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向. JavaScript 的一大特点是,函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念. 先来一个栗子: function fruits() {} fruits.prototype = { color: "red", say: function() { console

一道笔试题来理顺Java中的值传递和引用传递

前段时间参加了一场面试,其中有一道引用传递的题,因为当时并没有考虑清楚所以做错了. 现在来复盘一下,题目如下: private static void change(StringBuffer str11, StringBuffer str12) { str12 = str11; str11 = new StringBuffer("new world"); str12.append("new world");} public static void main(Stri

浅谈JavaScript中继承的实现

  谈到js中的面向对象编程,都有一个共同点,选择原型属性还是构造函数,两者各有利弊,而就片面的从js的对象创建以及继承的实现两个方面来说,官方所推荐的是两个相结合,各尽其责,各取其长,在前面的例子中,我已就在JavaScript中对象创建的方法做了一些总结,下面就其继承来道说一二:   1:原型链继承: 每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象内部的指针(默认的原型,所有默认类型都继承了Object,而这个继承也是用过原型链实现) fu

浅谈javascript中的闭包

引入定义:闭包只有权访问另一个函数中的作用域中的函数. 简单点说,就是当某函数a执行完毕后,闭包不会使得GC(JavaScript的回收机制)去回收a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量. 代码示例: window.onload = function(){ function createComparisonFunction(propertyName){ return function(object1, object2){ var value1 = object1[proper

浅谈javascript中的For in语法

相信大家都使用过javascript中的for循环,主要用来遍历数组对象,方便执行重复操作,体现代码的重用性.但是,应为数组的索引一般是整 型的数字,当遇到JSON对象或者object对象时,就不能使用for循环遍历了,应当使用for in函数遍历对象,这里就谈谈个人对for in的理解. 首先,虽然叫For in语法但关键字还是用for,这个语法还可以用来遍历对象,拿到的是对象的属性名称,然后通过对象名[属性名称]就可以拿到对象.个人觉得,理解这个语法的本质,关键在于理解每次循环得到的到底是什

浅谈JavaScript中的函数问题

前面的话:JavaScript可运行在所有主要平台的主流浏览器上,也可运行在每一个主流操作系统的服务器端上.所以呢,要想成为一名优秀的全栈工程师,必须懂得JavaScript语言.这是我整理的JS的部分函数问题,供大家参考借阅,有不妥的地方也请多多指教. 1.函数的三要素    1.1 函数的功能   1.2 函数的参数 a. 形参——定义函数的时候,没有实际的值,给实参占位 b. 实参——调用函数的时候,有实际的值: c. 当调用函数的时候会把实参复制一份传递给函数 d. 函数调用的时候,实参