一篇文章彻底搞懂es6 Promise

前言

Promise,用于解决回调地狱带来的问题,将异步操作以同步的操作编程表达出来,避免了层层嵌套的回调函数。

既然是用来解决回调地狱的问题,那首先来看下什么是回调地狱

var sayhello = function(callback){
    setTimeout(function(){
        console.log("hello");
        return callback(null);
    },1000);
}
sayhello(function(err){
    console.log("xiaomi");
});
console.log("mobile phone");

输出结果
mobile phone
hello
Xiaomi

看上面这段代码,假如我们需要对输出内容的顺序进行调整,例如依次打印xiaomi apple huawei ,那么我们之前的做法是怎么样的

var sayhello = function(name, callback){
    setTimeout(function(){
        console.log("hello");
        console.log(name);
        return callback(null);
    },1000);
}
sayhello("xiaomi", function(err){
    sayhello("apple", function(err){
        sayhello("huawei", function(err){
            console.log("end");
        });
    });
});
console.log("mobile phone");

问题很明显,代码层层嵌套,看起来十分的混乱,如果层级代码更多更是难以维护

因此Promise的出现使得我们可以用同步的方式来操作异步代码,解决以上问题

初识promise

var getUserInfo = function() {
    return new Promise(function(resolve) {
        setTimeout(function() {
            var user = {name: ‘Kerry Wu‘, age: 31};
            resolve(user);
        }, 3000);
    });
};
this.getUserInfo().then(function(userInfo) {
    console.log(‘userInfo‘,userInfo);//{ name: ‘Hanmeimei‘, age: 31 }
});

我们通过new关键词实例化一个Promise对象并返回该对象,然后使用.then的形式获取Promise返回的内容

这里需要注意的是,new Promise 实例化是一个同步的过程,而.then是一个异步的过程,关于同步异步执行顺序 ,先执行同步在执行异步代码

Promise状态

1、Pending 进行中 / Resolved 已成功 / Rejected 已失败

resove 将未完成变成已完成 pending => resolved

reject 将未完成变成已失败 pending => rejected

var promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

2、then 与 catch

then方法接收两个函数参数,第一个表示resove 已成功的回调,第二个表示reject 已失败的回调

用法如下:

var p = new Promise(function(resolve, reject){ ... })
p.then(function(){}, function(){})
p.then().catch();

then,前一个then的返回结果,可以再后一then的回调中获取,如:

var p3 = ()=> new Promise((resolve, reject)=>{
     resolve(‘{"name":"jack", "age":28}‘)
});

p3()
   .then(res => JSON.parse(res))
       .then(data => Object.assign(data, {name:‘rose‘}))
   .then(data => console.log(data))
// 输出:{name: "rose", age: 28}

异步加载图片

function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    var image = new Image();

    image.onload = function() {
      resolve(image);
    };

    image.onerror = function() {
      reject(new Error(‘Could not load image at ‘ + url));
    };

    image.src = url;
  });
}

loadImagesAsync(‘//img.static.com/xxx.jpg‘).then(function(img){
    //加载成功 显示图片
}, function(err){
   //加载失败 提示失败
})

异步加载数据

使用promise包装一个异步请,返回一个promise对象,使用then和catch的方式对返回结果进行处理

var getJSON = function(url){
  return new Promise((resolve, reject)=>{
             var client = new XMLHttpRequest();
          client.open(‘GET‘, url);
          client.onreadystatechange = callback;
               client.send();

          function callback(){
                  if(this.readyState !== 4) return;

                 if(this.status === 200){
                          resolve(this.response)
                  }else{
                          reject(new Error(this.statusText))
                     }
           }
   })
}

getJSON(‘/api/getList‘).then(function(data){
   //获取请求的数据
}, function(err){
     //请求失败错误处理
});

catch

p.catch()用于处理promise中rejected状态的回调,与p.then(resolveFn, rejectFn)中 rejectFn的作用相同

var p = new Promise(function(resolve, reject){ ... });
p.then(function(){}, function(){});
//等同于
p.then(function(){}).catch(function(){});

reject(‘error’) 与 throw new Error(‘…’) 都能被catch捕获

new Promise((resolve, reject) => {
        throw new Error(‘some error1‘);
}).catch(err =>  console.log(err.message))

// 等同于
new Promise((resolve, reject) => {
   reject(‘some error2‘)
}).catch(err => console.log(err))

捕获异常

promise对象的错误,具有 冒泡 性质,会一直向后传递,直到被捕获

推荐使用 catch 代替then(null, rejectFn)中的rejectFn,catch可以捕获前面then函数返回的错误信息,也更接近同步的写法

// bad
new Promise(function(resolve, reject){}).then(resolveFn, rejectFn)

// good
new Promise(function(resolve, reject){}).then(resoveFn).catch(rejectFn)

Promise all与race

Promise.all([]) 与 Promise.race([])

  • 接收一个数组做为参数,参数中的每个元素为promise实例,
  • 如果元素不是promise实例,则会调用Promise.resolve()转换为promise的实例
  • 将多个promise对象包装为一个新的promise对象

1、Promise.all()

Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调

当p1、p2、p3的状态全部为resolved时,才能将p的状态改为resolved

当p1、p2、p3其中一个状态变成rejected时,就会将p的状态变成rejected

var p = Promise.all([Promise.resolve(‘1‘), Promise.resolve(‘2‘), Promise.resolve(‘3‘)]);
p.then(data => console.log(data)) //["1", "2", "3"]

var p1 = Promise.all([Promise.resolve(‘1‘), Promise.reject(‘2‘), Promise.resolve(‘3‘)]);
p1.then(data => console.log(data)).catch(err => console.log(err)) // 2

Promise.all用的最多一般是我们在请求网络数据时,比如需要同时请求多个接口,我们可以合并多个请求一次处理

function getURL(URL) {
            return new Promise(function (resolve, reject) {
                var req = new XMLHttpRequest();
                req.open(‘GET‘, URL, true);
                req.onload = function () {
                    if (req.status === 200) {
                        resolve(req.responseText);
                    } else {
                        reject(new Error(req.statusText));
                    }
                };
                req.onerror = function () {
                    reject(new Error(req.statusText));
                };
                req.send();
            });
        }
        function getComment() {
            return getURL(‘http://azu.github.io/promises-book/json/comment.json‘).then(res=>JSON.parse(res));
        }
        function getPeople() {
            return getURL(‘http://azu.github.io/promises-book/json/people.json‘).then(res=>JSON.parse(res));
        }
        // 合并请求
        Promise.all([getComment(), getPeople()]).then(function (value) {
            console.log(value);
        }).catch(function(error){
            console.log(error);
        });

Promise resolve和reject

Promise.resolve() 与 Promise.reject()

Promise.resolve(‘foo‘)
// 等价于
new Promise(resolve => resolve(‘foo‘))

最后

记得以前在面试的时候,被问了一道很有意思的面试题,主要是考察promise和settimeout执行顺序

setTimeout(function () {
    console.log(1)
}, 0);
new Promise(function executor(resolve) {
        resolve();
}).then(function () {
    console.log(2);
});

如上代码,为什么运行结果是2,1而不是1,2?

不是setTimeout先加入任务队列吗?

解答:

1、从规范上来讲,setTimeout有一个4ms的最短时间,也就是说不管你设定多少,反正最少都要间隔4ms才运行里面的回调(当然,浏览器有没有遵守这个规范是另外一回事儿)。而Promise的异步没有这个问题。

2、从具体实现上来说,这俩的异步队列不一样,Promise所在的那个异步队列优先级要高一些。

还有一道差不多的

(function test() {
    setTimeout(function() {console.log(4)}, 0);
    new Promise(function executor(resolve) {
        console.log(1);
        for( var i=0 ; i<10000 ; i++ ) {
            i == 9999 && resolve();
        }
        console.log(2);
    }).then(function() {
        console.log(5);
    });
    console.log(3);
})()

为什么输出结果是 1,2,3,5,4 而非 1,2,3,4,5 ?

解答:

1、Promise.then 是异步执行的,而创建Promise实例( executor )是同步执行的。

2、setTimeout 的异步和 Promise.then 的异步不太一样不在同一个队列中,setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.then()在本轮“事件循环”结束时执行。因此then 函数先输出,settimeout后输出。

这里涉及到js事件循环、任务队列的东西,了解更多 https://www.cnblogs.com/hity-tt/p/6733062.html

参考阅读

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/0014345008539155e93fc16046d4bb7854943814c4f9dc2000

http://coderlt.coding.me/2016/07/17/ES6-promise/

原文地址:https://www.cnblogs.com/fozero/p/10423830.html

时间: 2024-08-28 17:45:12

一篇文章彻底搞懂es6 Promise的相关文章

海明码一篇文章彻底搞懂

海明码学习前提 记住几个要点: 不要用异或套用公式!!!!题目随便变几个变死你! 看完这篇博客不要看别的博客!!!!别的人瞎写的坑死你 学习海明码之前,我们要约定3个原则: 海明码只能检测出2位错,纠1位错(因此不要问如果3位错怎么办等幼稚问题). 海明码默认进行偶校验(除非特殊说明使用奇校验). 海明码是一串由0和1组成的序列(除01外没有其他的值,记住了!这是重点) 如果下面有任何无法理解的问题,反复看上面三个原则,下面再也不赘述. 前提:奇偶校验 奇校验:这串序列1的个数如果为偶数则在前面

一篇文章彻底搞懂Java虚拟机

概念 虚拟机:指以软件的方式模拟具有完整硬件系统功能.运行在一个完全隔离环境中的完整计算机系统 ,是物理机的软件实现.常用的虚拟机有VMWare,Visual Box,Java Virtual Machine(Java虚拟机,简称JVM). Java虚拟机阵营:Sun HotSpot VM.BEA JRockit VM.IBM J9 VM.Azul VM.Apache Harmony.Google Dalvik VM.Microsoft JVM- 启动流程 基本架构 Java运行时编译源码(.j

一篇文章彻底搞懂snowflake算法及百度美团的最佳实践

写在前面的话 一提到分布式ID自动生成方案,大家肯定都非常熟悉,并且立即能说出自家拿手的几种方案,确实,ID作为系统数据的重要标识,重要性不言而喻,而各种方案也是历经多代优化,请允许我用这个视角对分布式ID自动生成方案进行分类: 实现方式 完全依赖数据源方式 ID的生成规则,读取控制完全由数据源控制,常见的如数据库的自增长ID,序列号等,或Redis的INCR/INCRBY原子操作产生顺序号等. 半依赖数据源方式 ID的生成规则,有部分生成因子需要由数据源(或配置信息)控制,如snowflake

一个简单的例子搞懂ES6之Promise

ES5中实现异步的常见方式不外乎以下几种: 1. 回调函数 2. 事件驱动 2. 自定义事件(根本上原理同事件驱动相同) 而ES6中的Promise的出现就使得异步变得非常简单.promise中的异步是这样的: * 每当我需要执行一次异步操作的时候,我都需要new一个promise对象 * 每一个异步操作的Promise对象都需要设定一个成功执行的条件和成功的回调.一个失败的条件和失败的回调 * Promise对象可通过执行then()方法获得成功的回调信息 * Promise对象可通过执行ca

【Networkk】一篇文章完全搞清楚 scoket read/write 返回码、阻塞与非阻塞、异常处理 等让你头疼已久的问题

浅谈TCP/IP网络编程中socket的行为 我认为,想要熟练掌握Linux下的TCP/IP网络编程,至少有三个层面的知识需要熟悉: 1. TCP/IP协议(如连接的建立和终止.重传和确认.滑动窗口和拥塞控制等等) 2. Socket I/O系统调用(重点如read/write),这是TCP/IP协议在应用层表现出来的行为. 3. 编写Performant, Scalable的服务器程序.包括多线程.IO Multiplexing.非阻塞.异步等各种技术. 关于TCP/IP协议,建议参考Rich

3分钟带你搞懂ES6 import 和 export

如下语句是 default import: // B.js import A from './A' 且只在A存在 default export 时生效: // A.js export default 42 这种情况下你用import语句, 随便取什么名字都没关系: // B.js import A from './A' import MyA from './A' import Something from './A' 因为他最终解析的是A.js 的 default export. 如下是命名为A

一篇文章彻底弄懂Base64编码原理

在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. Base64的由来 目前Base64已经成为网络上常见的传输8Bit字节代码的编码方式之一.在做支付系统时,系统之间的报文交互都需要使用Base64对明文进行转码,然后再进行签名或加密,之后再进行(或再次Base64)传输.那么,Base64到底起到什么作用呢? 在参数传输的过程中经常遇到的一种情况:使用全英文的没问题,但一旦涉及到中文就会出现

知识扩展——(转)一篇文章彻底弄懂Base64编码原理

在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. 一.Base64的由来 目前Base64已经成为网络上常见的传输8Bit字节代码的编码方式之一.在做支付系统时,系统之间的报文交互都需要使用Base64对明文进行转码,然后再进行签名或加密,之后再进行(或再次Base64)传输.那么,Base64到底起到什么作用呢? 在参数传输的过程中经常遇到的一种情况:使用全英文的没问题,但一旦涉及到中文就会

一篇文章搞懂python2、3编码

说在前边: 编码问题一直困扰着每一个程序员的编程之路,如果不将它彻底搞清楚,那么你的的这条路一定会走的格外艰辛,尤其是针对使用python的程序员来说,这一问题更加显著, 因为python有两个版本,这两个版本编码格式却完全不同,但我们却经常需要兼顾这两个版本,所以出现各种问题的几率就大了很多. 所以在这里我试图用一篇文章来彻底梳理整个python语言的编码问题,尽量降低以后在这方面举到问题的可能性. ps 此文一定程度上参考和引用了alex的博客:“https://www.cnblogs.co