浅谈promise和js执行机制(一)

郜小超 等级 442 1 1

作为一个入门级前端,今天是一个非常值得纪念的日子,因为这是我第一次在论坛上发表帖子,作为起步。虽然我觉得自己水平还是十分的有限,对一些细节的理解还不是很透彻,但是还是要迈出这一步,不管是给别的新手作为学习参考,还是自己以后回顾,总觉得需要把自己的成长记录下来,希望自己以后还是要多坚持,如果有不对的地方还是希望大家及时提出来,共同进步

今天有时间翻到了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和js执行机制(一)

看完上面的代码大家可能会有好多疑问,我在这里把我当时初学promise的疑问和大家分享一下:

(1)为什么我要在promise对象外面需要用一个函数来包裹起来呢?

答:Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

这是promise的一些缺点,一旦新建它就会立即执行 所以为了控制这个promise对象什么时候执行,在开发过程中我们需要在外面包裹一个函数,通过调用函数的形式来控制promise的执行。大家可以在自己的编辑器中试试。

new Promise(function(resolve, reject) {
    setTimeout(()=> {
        console.log('开车!!!')
    },2000)
});

浅谈promise和js执行机制(一)

(2)在每一个函数中我为什么要将我的promise对象用一个变量接收然后return出去呢?

答:因为有了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对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数

(3)如何判断失败的情况呢?

//做饭
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 的回调。

(4)如何在多个异步操作都完成后才执行回调呢?

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。所以上面代码的输出结果就是:

浅谈promise和js执行机制(一)

这是.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)
})

结果如下: 浅谈promise和js执行机制(一) 可以看出来在甲执行完毕后立即就执行了.race()方法,但是不耽误其他两个异步操作的进行,.race()中拿到的参数也只是当前最先执行完的异步操作中传递出来的参数。

(4)由promise联系到的js的宏任务和微任务

在看到promise的时候有一个地方还是令我有困惑,现在先留一个悬念,大家可以先看看下面这段代码,你觉得输出结果是什么呢? 我们下回见!

setTimeout(function(){
  console.log('1')
});
new Promise(function(resolve){
    console.log('2');
    resolve();
}).then(function(){
    console.log('3')
});
console.log('4');
收藏
评论区

相关推荐

一、手写源码之 Promise
版本一,构造函数 javascript function MyPromise(fn () {}) { // const this {} this.state 'pending' this.value undefined const resolve (value) { if (this.state
《javascript高级程序设计》核心知识总结
此文是对js高级程序设计一书难点的总结,也是笔者在看了3遍之后的一些梳理和感想,希望能借此巩固js的基础和对一些核心概念有更深入的了解。 摘要 js基本的数据类型和关键点 变量,作用域和内存问题 垃圾回收机制 面向对象的程序设计 实现类与继承的经典方式 BOM和DOM对象 DOM扩展与高级API介绍 高级编程技巧 跨文档消息传递和aja
浅谈promise和js执行机制(一)
作为一个入门级前端,今天是一个非常值得纪念的日子,因为这是我第一次在论坛上发表帖子,作为起步。虽然我觉得自己水平还是十分的有限,对一些细节的理解还不是很透彻,但是还是要迈出这一步,不管是给别的新手作为学习参考,还是自己以后回顾,总觉得需要把自己的成长记录下来,希望自己以后还是要多坚持,如果有不对的地方还是希望大家及时提出来,共同进步 今天有时间翻到了
浅谈promise和js执行机制(二)
让我们继续上一次遗留的问题: setTimeout(function(){ console.log('1') }); new Promise(function(resolve){ console.log('2'); resolve(); }).then(function(){ console.log('3') }); conso
Android webview 与 js(Vue) 交互
js 与原生交互分为两种情况:js 调用原生方法,原生调用 js 方法。 本文将对这两种情况分别讲解,H5 端用 vue 实现。 一、前期准备(Vue项目准备) 本文的 H5 端用Vue 实现,所以在正式开始前先把 Vue 项目环境准备好。 项目写好后,执行 npm run serve 命令启动项目,启动成功后会在命令
Promise从入门到拿Offer之手写Promise
1、Promise构造函数的实现Promise构造函数用来声明示例对象,需要传入一个执行器函数。其中包括resolve函数和reject函数,以及几个重要的属性:状态属性、结果属性和回调函数队列。构造函数的基本框架 resolve函数用于异步处理成功后调用的函数。其中包括验证对象状态修改次数,修改promise实例对象状态,异步调用成功的回调函数
js异步的5种样式
js异步的5种样式 1.定时器 2.AJAX 3.Promise 4.Generator 5.asyns和awite  1.定时器     setTimeout() : 延时器        可以传入三个分别是            1)code:必
用 async/await 来处理异步
一级标题昨天看了一篇vue的教程,作者用async/ await来发送异步请求,从服务端获取数据,代码很简洁,同时async/await 已经被标准化,是时候学习一下了。先说一下async的用法,它作为一个关键字放到函数前面,用于表示函数是一个异步函数,因为async就是异步的意思, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行。 写一个async
理解 Javascript 中的 Async / Await
在本文中,我们将探讨async/await,对于每个Javascript开发人员来说,是异步编程的首选工具。如果您不熟悉javascript,请不要担心,本文将帮助您async/await从头开始理解。 介绍async/await 是javascript中的一种模式,可使您的代码以同步方式执行,但又不影响javascript的异步行为。 定义异步功能要定义一
你不可不知的JS面试题(第二期)
1、什么是继承?子类可以使用父类的所有功能,并且对功能进行扩展。 新增方法 改用方法 (1)、ES6使用extends子类继承父类的方法。 // 父类     class A         constructor(name)             this.name name;                  getNa
你要的几个JS实用工具函数(持续更新)
今天,我们来总结下我们平常使用的工具函数,希望对大家有用。1、封装fetch「源码:」/   封装fetch函数,用Promise做回调   @type get: (function()), post: (function(, ))  / const fetchUtil       get: (url)           return new 
前端 - 常见的异常捕获方法
前端异常捕获在ES3之前js代码执行的过程中,一旦出现错误,整个js代码都会停止执行,这样就显的代码非常的不健壮。从ES3开始,js也提供了类似的异常处理机制,从而让js代码变的更健壮,程序执行的过程中出现了异常,也可以让程序具有了一部分的异常恢复能力。js异常的特点是,出现不会导致JS引擎崩溃,最多只会终止当前执行的任务。回归正题,我们该如何在程序异常发生
从 生成器 到 promise+async
本文主要讲解js中关于生成器的相关概念和作用,以及到后面结合 promise 实现 es7中的 async 原理,你将学习到js中异步流程控制相关知识 1、认识生成器思考如下代码:javascript let x 1 function foo() x++ bar() console.log(x) // 3 function bar(
Vue 重复进入相同路由警报
路由守卫重复进入两次,报错(虽然页面还可以运行) 原因:vuerouter路由版本更新产生的问题,导致路由跳转失败抛出该错误,但并不影响程序功能 Uncaught (in promise) Error: Redirected when going from "/productDetail?VNK326acc75" to "/productTerms" via
nextTick原理解析
nextTick 是什么\$nextTick:根据官方文档的解释,它可以在 DOM 更新完毕之后执行一个回调函数,并返回一个 Promise(如果支持的话)js// 修改数据vm.msg "Hello";// DOM 还没有更新Vue.nextTick(function() // DOM 更新了);这块理解 EventLoop 的应该一看就懂,其实就是