去年6月,ES2015正式发布(即ES6,这是它的诞生名称),其中Promise被列为正式规范。作为ES6中最重要的特性之一,
首先,我们需要彻底掌握和理解。本文将从浅入深地解释Promise的基本概念和用法。
ES6承诺,先拉出来走走
先不谈复杂的概念。让我们简单粗暴地使用Promise来获得直观的感受。那么第一个问题来了,什么是Promise?是一节课吗?反对?数组?功能?
不要猜,直接打印出来,console.dir(答应),太简单粗暴了。
乍一看可以理解为Promise是一个构造函数,有大家熟悉的all、reject、resolve等方法,也有大家熟悉的then、catch等方法上原型。所以,必须有随Promise new一起出现的对象的然后捕获方法,没错。
那我们就玩新的吧。
var p=new promise(function(resolve,reject){//做一些异步操作settimeout(function(){ console . log('执行完成'));解析(“任何数据”);}, 2000);});Promise的构造函数接收一个参数,这个参数是一个函数,传入两个参数:resolve和reject,分别表示异步操作成功执行后的回调函数和异步操作失败后的回调函数。其实用“成功”和“失败”来形容是不准确的。根据标准,解决是将“承诺”的状态设置为“已完成”,拒绝是将“承诺”的状态设置为“已拒绝”。但是,我们可以在一开始就理解这一点,然后仔细研究这个概念。
在上面的代码中,我们执行了一个异步操作,即setTimeout。2秒钟后,我们输出“执行完成”并调用resolve方法。
运行代码将在2秒后输出“执行完成”。注意!我只是新买了一个东西,没有叫它。我们传入的函数已经被执行了。这是一个需要注意的细节。因此,当我们使用Promise时,我们通常将其包装在一个函数中,并在必要时运行它,例如:
函数runasync () {var p=newpromise(函数(解析,拒绝){//做一些异步操作settimeout(函数(){console.log('执行完成')));解析(“任何数据”);}, 2000);});返回p;}runAsync()这时,你应该有两个问题:1。包装这样的功能有什么用?2 .解析(“无论什么数据”);这是干性头发吗?
我们继续吧。在我们包装的函数的末尾,将返回一个Promise对象,也就是说,我们通过执行这个函数得到一个Promise对象。还记得Promise对象上的then和catch方法吗?这就是力量,看看下面的代码:
runAsync()。然后(函数(数据){ console.log(数据);//您可以稍后对传输的数据进行一些其他操作。//.});在runAsync()返回时直接调用then方法。然后接收一个参数,它是一个函数,并获取我们在runAsync中调用resolve时传递的参数。运行这段代码将在2秒钟后输出“执行完成”,然后是“任何数据”。
此时,您应该意识到,然后中的函数就像我们通常的回调函数一样,可以在执行异步任务runAsync之后执行。这就是无极的功能。简单来说,就是在异步操作执行后,可以将原来的回调编写分离出来,通过链式调用来执行回调函数。
你可能不屑一顾,所以这是Promise能做的最好的了?对我来说,封装回调函数并将其传递给runAsync不是一样的吗,如下所示:
函数runasync(回调){settimeout(函数(){console.log('执行完成');回调('无论什么数据');}, 2000);}runAsync(函数(数据){ console.log(数据);});效果是一样的,但是为什么要费心用Promise。那么问题来了,如果有多个回调怎么办?如果回调也是异步操作,执行后需要相应的回调函数怎么办?您不能定义回调2并将其传递给回调。Promise的优点是可以继续在then方法中写入Promise对象并返回,然后继续调用then进行回调操作。
连锁经营的使用
所以,从表面上看,Promise只能简化逐层回调的编写,但本质上,Promise的本质是“状态”。及时调用回调函数比传递回调函数更简单、更灵活。所以使用Promise的正确场景是这样的:
runAsync1()。然后(函数(数据){ console.log(数据);返回runasync 2();}).然后(函数(数据){ console.log(数据);返回runasync 3();}).然后(函数(数据){ console.log(数据);});这样,每个异步回调的内容可以按顺序每两秒输出一次,在runAsync2中发送解析的数据可以在下一个then方法中获得。运行结果如下:
猜猜runAsync1、runAsync2和runAsync3是如何定义的。没错,是这样的:
函数runasync1 () {var p=newpromise(函数(resolve,reject){//做一些异步操作settimeout(函数(){console.log('异步任务1执行完成'));解析('无论什么数据1 ');}, 1000);});返回p;} function runasync 2(){ var p=new promise(function(resolve,reject){//做一些异步操作settimeout(function(){ console . log('异步任务2执行完成'));解析(‘无论数据2’);}, 2000);});返回p;} function runasync 3(){ var p=new promise(function(resolve,reject){//做一些异步操作settimeout(function(){ console . log('异步任务3执行完成'));解析('无论什么数据3 ');}, 2000);});返回p;}在then方法中,也可以直接返回数据,而不是Promise对象,可以在后面的then中接收数据。例如,我们将上述代码修改如下:
runAsync1()。然后(函数(数据){ console.log(数据);返回runasync 2();}).然后(函数(数据){ console.log(数据);返回“直接返回数据”;//直接在这里返回数据})。然后(函数(数据){console.log(数据);});然后输出变成这样:
拒绝的用法
在这里,你应该对“什么是承诺”有一个基本的了解。然后,让我们看看ES6的承诺的其他功能。我们只使用了决心,但还没有使用拒绝。它是做什么的?事实上,我们前面的例子都是“成功执行”且没有“失败”的回调。拒绝的功能是将Promise的状态设置为拒绝,这样我们就可以在这时捕捉到它,然后执行带有“失败”的回调。看看下面的代码。
函数get number(){ var p=new promise(function(resolve,reject){//做一些异步操作settimeout(function(){ var num=math . ceil(math . random()* 10));//如果(num=5){ resolve(num)从1到10,则生成一个随机数;} else{ reject('数字太大');} }, 2000);});返回p;}getNumber()。然后(函数(数据){ console . log(' resolved ');console.log(数据);},函数(原因,数据){ console.log('拒绝');console.log(原因);});getNumber函数用于异步获取一个数字,2秒后执行完成。如果该数字小于或等于5,我们认为它“成功”,并调用resolve来修改Promise的状态。否则,我们将其视为“失败”,调用reject并传递一个参数作为失败的原因。
运行getNumber,然后传入两个参数。那么方法可以接受两个参数,第一个对应于resolve的回调,第二个对应于reject的回调。所以我们可以分别从他们那里得到数据。多次运行此代码,您将获得以下两个随机结果:
或者
渔获物的使用
我们知道Promise对象除了then方法之外还有一个catch方法。它是用来做什么的?事实上,它与then的第二个参数相同,用于指定reject的回调。用法如下:
getNumber()。然后(函数(数据){ console . log(' resolved ');console.log(数据);}).catch(函数(原因){ console.log('拒绝');console.log(原因);});效果与在then的第二个参数中写入的效果相同。但是,它还有另一个功能:如果在resolve的回调过程中抛出异常(代码错误)(即上面第一个参数),js不会被报告为陷入错误,而是会进入这个catch方法。请参见以下代码:
getNumber()。然后(函数(数据){ console . log(' resolved ');console.log(数据);console . log(some data);//此处未定义某些数据})。catch(函数(原因){console.log('拒绝');console.log(原因);});在解析回调中,我们使用console . log(some data);变量somedata未定义。如果我们不使用Promise,代码在这里运行时会直接在控制台报错,不会向下运行。但是在这里,你会得到这样的结果:
也就是说,它进入了catch方法,并将错误原因传递给了reason参数。即使有错误的代码也不会报告错误,这与我们的try/catch语句具有相同的功能。
所有的用法
Promise的all方法提供了并行执行异步操作的能力,回调在所有异步操作执行完毕后才会执行。我们仍然使用上面定义的三个函数:runAsync1、runAsync2和runAsync3。请参见以下示例:
Promise.all([runAsync1()、runAsync2()、runAsync3()])。然后(函数(结果){ console.log(结果);});使用Promise.all执行,all接收一个数组参数,其中的值最终返回给Promise对象。这样,三个异步操作的并行执行在它们全部执行之前不会进行。那么,三个异步操作返回的数据在哪里呢?那就全进去了。All将把所有异步操作的结果放入一个数组中,然后发送给then,这就是上面的结果。所以上面代码的输出是:
有了这一切,你可以并行执行多个异步操作,并在一次回调中处理所有返回的数据,这不是很酷吗?有一个场景非常适合这个。有些游戏有更多的素材。打开网页时,会预加载图片、flash、各种静态文件等各种资源。加载完所有内容后,我们将初始化页面。
种族的用法
所有方法的效果其实都是“谁跑得慢就执行回调”,所以还有一种方法“谁跑得快就执行回调”,这就是race方法,这个词本来就是race的意思。种族的用法和所有的一样。让我们将上面runAsync1的延迟更改为1秒,以查看:
Promise.race([runAsync1()、runAsync2()、runAsync3()])。然后(函数(结果){ console.log(结果);});这三个异步操作也并行执行。因此,您应该能够猜到runAsync1已经在1秒钟后被执行,然后被执行。结果是这样的:
你猜对了吗?不完全是,是吗?当中的回调开始执行时,RunAsync2()和runAsync3()没有停止,它们仍然再次执行。然后,一秒钟后,他们结束的标志被输出。
这场比赛有什么用?还有很多使用场景。例如,我们可以使用race为异步请求设置超时,并在超时后执行相应的操作。代码如下:
//请求图片资源functionrequest img(){ var p=new promise(function(解析,拒绝){ var img=new image();img . onload=function(){ resolve(img);} img.src='