摘要:如果改变已经发生了,你再对对象添加回调函数,也会立即得到这个结果。其次,如果不设置回调函数,内部抛出的错误,不会反应到外部。
作为一个入门级前端,今天是一个非常值得纪念的日子,因为这是我第一次在论坛上发表帖子,作为起步。虽然我觉得自己水平还是十分的有限,对一些细节的理解还不是很透彻,但是还是要迈出这一步,不管是给别的新手作为学习参考,还是自己以后回顾,总觉得需要把自己的成长记录下来,希望自己以后还是要多坚持,如果有不对的地方还是希望大家及时提出来,共同进步
今天有时间翻到了es6的promise,可能大家都对此熟悉不过,我之前一直觉得promise也很简单,但是今天确实让我对promise有了一个新的了解,以前的理解可能是错误的。。。先来看看官方的promise的定义是:
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
特点:
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
初读上面这段话我读了不下5次,但是我还是没能真正了解其真正表达的意思,于是我查阅了部分资料,终于找到了一个比较容易理解的说法,这个估计小白应该是可以看得懂得。
就拿做饭吃饭洗碗来举例子吧,这是三个步骤,第一步做饭,再第二步吃饭的时候我们需要拿到第一步做的饭,在第三步洗碗的时候我们需要拿到第二步的碗筷,而且这三个步骤必须是按照顺序执行,有严格的先后顺序。
//做饭
function cook(){
console.log("开始做饭。");
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log("做饭完毕!");
resolve("鸡蛋炒饭");
}, 1000);
});
return p;
}
//吃饭
function eat(data){
console.log("开始吃饭:" + data);
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log("吃饭完毕!");
resolve("一个碗和一双筷子");
}, 2000);
});
return p;
}
//洗碗
function wash(data){
console.log("开始洗碗:" + data);
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log("洗碗完毕!");
resolve("干净的碗筷");
}, 2000);
});
return p;
}
//函数调用
cook().then(res1 => {
console.log(res1,"这是第一步传递给第二步的参数")
return eat(res1)
}).then(res2 => {
console.log(res2,"这是第二步传给第三步的参数")
return wash(res2)
}).then(res3 => {
console.log(res3,"饭吃完了还你干净的碗筷")
})
结果如下:
看完上面的代码大家可能会有好多疑问,我在这里把我当时初学promise的疑问和大家分享一下:
答:Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
这是promise的一些缺点,一旦新建它就会立即执行 所以为了控制这个promise对象什么时候执行,在开发过程中我们需要在外面包裹一个函数,通过调用函数的形式来控制promise的执行。大家可以在自己的编辑器中试试。
new Promise(function(resolve, reject) {
setTimeout(()=> {
console.log("开车!!!")
},2000)
});
答:因为有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
//函数调用
cook().then(res1 => {
console.log(res1,"这是第一步传递给第二步的参数")
return eat(res1)
}).then(res2 => {
console.log(res2,"这是第二步传给第三步的参数")
return wash(res2)
}).then(res3 => {
console.log(res3,"饭吃完了还你干净的碗筷")
})
大家看我的这段代码 在执行了cook()函数的时候cook函数return出来一个promise对象,promise对象上有.then()或.catch()方法,那么直接cook().then就可以在.then()方法中我们可以拿到promise对象中向外传递的参数,这个参数我们将传递给下一个eat()函数,作为eat()函数的参数继续执行。 而eat()函数也return了一个promise对象。那么我们将我们的这段代码拆解一下:
第一步: cook() //拿到的是cook return出来的promise对象
第二步: cook().then(res => {
console.log(res) //拿到内部向外部传递的参数
})
第三步: cook().then(res => {
console.log(res)
return eat(res) //eat执行以后return的是eat的promise对象,然后再把这个对象继续向外return
}) //那么第三步的最终结果就是eat()的promise对象
第四步: cook().then(res => {
console.log(res)
return eat(res) //此时的结果是eat()的promise对象
}).then(res2 => {
// 此时eat的promise又有.then方法 .....以此类推
})
我们就这样一步一步的完成了整个做饭、吃饭、洗碗的整个流程。 纵观以上代码你会发现虽然每一个操作流程中我都是以异步函数setTimeout来进行的,但是在调用过程中确是按照 做饭-吃饭-洗碗的正常流程表达的。这不是promise的有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
//做饭
function cook(){
console.log("开始做饭。");
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log("饭糊了....没法吃");
reject("糊了的饭");
}, 1000);
});
return p;
}
//吃饭
function eat(data){
console.log("开始吃饭:" + data);
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log("吃饭完毕!");
resolve("一个碗和一双筷子");
}, 2000);
});
return p;
}
cook().then(res1 => {
console.log(res1,"这是第一步传递给第二步的参数")
return eat(res1)
}).catch(err => {
console.log(err,"返回错误")
})
catch()方法用来指定 reject 的回调。
function tackBus1(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log("甲正在上车");
resolve("甲坐好了");
}, 1000);
});
return p;
}
function tackBus2(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log("乙正在上车");
resolve("乙坐好了");
}, 2000);
});
return p;
}
function tackBus3(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log("丙正在上车");
resolve("丙坐好了");
}, 3000);
});
return p;
}
Promise.all([tackBus1(),tackBus2(),tackBus3()]).then(res => {
console.log(res)
})
司机在等人上车,在乘客都坐稳了以后开车发车,这就用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面呢,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。所以上面代码的输出结果就是:
这是.all的方法,promise还有一种.race的方法,它和all方法的区别就是: all方法的效果实际上是谁跑的慢,以谁为准执行回调,那么相对的就有另一个方法谁跑的快,以谁为准执行回调,刚刚的执行结果我设置了他们的时间间隔分别是1s,2s,3s,在等最慢的执行完以后才执行了all这个回调方法,现在咱们来试试promise.race方法
function tackBus1(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log("甲正在上车");
resolve("甲坐好了");
}, 1000);
});
return p;
}
function tackBus2(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log("乙正在上车");
resolve("乙坐好了");
}, 2000);
});
return p;
}
function tackBus3(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log("丙正在上车");
resolve("丙坐好了");
}, 3000);
});
return p;
}
Promise.race([tackBus1(),tackBus2(),tackBus3()]).then(res => {
console.log(res)
})
结果如下:
可以看出来在甲执行完毕后立即就执行了.race()方法,但是不耽误其他两个异步操作的进行,.race()中拿到的参数也只是当前最先执行完的异步操作中传递出来的参数。
在看到promise的时候有一个地方还是令我有困惑,现在先留一个悬念,大家可以先看看下面这段代码,你觉得输出结果是什么呢? 我们下回见!
setTimeout(function(){
console.log("1")
});
new Promise(function(resolve){
console.log("2");
resolve();
}).then(function(){
console.log("3")
});
console.log("4");
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/6835.html
摘要:事件循环背景是一门单线程非阻塞的脚本语言,单线程意味着,代码在执行的任何时候,都只有一个主线程来处理所有的任务。在意识到该问题之际,新特性中的可以让成为一门多线程语言,但实际开发中使用存在着诸多限制。这个地方被称为执行栈。 事件循环(Event Loop) 背景 JavaScript是一门单线程非阻塞的脚本语言,单线程意味着,JavaScript代码在执行的任何时候,都只有一个主线程来...
摘要:如果没有其他异步任务要处理比如到期的定时器,会一直停留在这个阶段,等待请求返回结果。执行的执行事件关闭请求的,例如事件循环的每一次循环都需要依次经过上述的阶段。因此,才会早于执行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任务(Synchronous) 在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务 ...
摘要:声明的变量不得改变值,这意味着,一旦声明变量,就必须立即初始化,不能留到以后赋值。 虽然今年没有换工作的打算 但为了跟上时代的脚步 还是忍不住整理了一份最新前端知识点 知识点汇总 1.HTML HTML5新特性,语义化浏览器的标准模式和怪异模式xhtml和html的区别使用data-的好处meta标签canvasHTML废弃的标签IE6 bug,和一些定位写法css js放置位置和原因...
摘要:声明的变量不得改变值,这意味着,一旦声明变量,就必须立即初始化,不能留到以后赋值。 虽然今年没有换工作的打算 但为了跟上时代的脚步 还是忍不住整理了一份最新前端知识点 知识点汇总 1.HTML HTML5新特性,语义化浏览器的标准模式和怪异模式xhtml和html的区别使用data-的好处meta标签canvasHTML废弃的标签IE6 bug,和一些定位写法css js放置位置和原因...
阅读 1290·2019-08-30 15:44
阅读 1964·2019-08-30 13:49
阅读 1634·2019-08-26 13:54
阅读 3467·2019-08-26 10:20
阅读 3213·2019-08-23 17:18
阅读 3277·2019-08-23 17:05
阅读 2087·2019-08-23 15:38
阅读 992·2019-08-23 14:35