Promise晋级—完全吃透
栏目: JavaScript · 发布时间: 6年前
内容简介:基本的promise使用,读本文需要了解基本的查兼容性 基本上 主流浏览器支持没有问题。
Promise晋级,需要的全部都在这
主要内容:
- promise基本实现原理
- promise 使用中难点(链式调用,API基本上返回都是一个新Promise,及参数传递)
- promise 对异常处理
- promise 简单实现及规范
参考:
阮一峰ES6入门
0. 基本用法
基本的promise使用,读本文需要了解基本的 Promise
使用。
1. 兼容性
查兼容性 基本上 主流浏览器支持没有问题。
IE不兼容 问题,本文不予以处理,出门左转,找谷哥。具体查看babel,或者 自己实现一个Promise
2. ajax XMLHttpRequest封装
//get 请求封装 function get(url) { // Return a new promise. return new Promise(function(resolve, reject) { // Do the usual XHR stuff var req = new XMLHttpRequest(); req.open('GET', url); req.onload = function() { // This is called even on 404 etc // so check the status if (req.status == 200) { // Resolve the promise with the response text resolve(req.response); } else { // Otherwise reject with the status text // which will hopefully be a meaningful error reject(Error(req.statusText)); } }; // Handle network errors req.onerror = function() { reject(Error("Network Error")); }; // Make the request req.send(); }); } 复制代码
1. Promse API
Promise API 分为 :MDN
-
静态方法
-
prototype
上方法Promise.prototype.then()
来分析首先来看看 `Promise.prototype.then()`返回一个`Promise`,但`Promise`内部有返回值,且 返回值,可以是个值,也可能就是一个新`Promise` *具体规则如下:* 复制代码
- 如果then中的回调函数返回一个值,那么then返回的Promise将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。
- 如果then中的回调函数抛出一个错误,那么then返回的Promise将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
- 如果then中的回调函数返回一个已经是接受状态的Promise,那么then返回的Promise也会成为接受状态,并且将那个Promise的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。
- 如果then中的回调函数返回一个已经是拒绝状态的Promise,那么then返回的Promise也会成为拒绝状态,并且将那个Promise的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。
- 如果then中的回调函数返回一个未定状态(pending)的Promise,那么then返回Promise的状态也是未定的,并且它的终态与那个Promise的终态相同;同时,它变为终态时调用的回调函数参数与那个Promise变为终态时的回调函数的参数是相同的。
上面是官方规则,神马,具体白话就是 核心是 返回参数及返回promise的状态
参考:MDN
是不是 觉得很晕,没关系,可以先看 下一节,看完后,再回过来看具体的说明
/*then 回调中, 1. 返回是return function,则返回一个Promise 【参见对比3代码】 2. 不是一个function,则 then 将创建一个没有经过回调函数处理的新 Promise 对象,这个新 Promise 只是简单地接受调用这个 then 的原 Promise 的终态作为它的终态。(MDN中解释)【参见对比1代码】 3. 返回一个function,但没有return ,则相当于 then(null) */ //对比1 穿透问题 返回是'foo' 而不是 'bar' Promise.resolve('foo').then(Promise.resolve('bar')).then(function(result){ console.log(result) }) //对比2 打印undefined Promise.resolve('foo').then(function(){Promise.resolve('bar')}).then(function(result){ console.log(result) }) //对比3 返回 'bar' Promise.resolve('foo').then(function() { return Promise.resolve('bar') }).then(function(result) { console.log(result) }) 复制代码
2. Prmise 链式调用
链式调用
- 核心就是 then catch 等方法返回一个Promise
- 链式 调用数据传递(注意)
1. 值传递问题
简单例子
//正常状态 const promise1 = new Promise((resolve, reject) => { resolve('0000')// }) promise1.then(result => { console.log(result) //0000 return '1111';//类似于 return Promise.resolve('1111'); 参数是data,promise 状态时 resolve }).then(data => { console.log(data) // 1111 }) 复制代码
一个实际的例子:(拿来大神的例子 JavaScript Promise:简介 )
get('story.json').then(function(response) { console.log("Success!", response); }) 复制代码
//这里的 response 是 JSON,但是我们当前收到的是其纯文本。也可以设置XMLHttpRequest.responseType =json get('story.json').then(function(response) { return JSON.parse(response); }).then(function(response) { console.log("Yey JSON!", response); }) 复制代码
//由于 JSON.parse() 采用单一参数并返回改变的值,因此我们可以将其简化为: get('story.json').then(JSON.parse).then(function(response) { console.log("Yey JSON!", response); }) 复制代码
function getJSON(url) { return get(url).then(JSON.parse); } //getJSON() 仍返回一个 promise,该 promise 获取 URL 后将 response 解析为 JSON。 复制代码
2. 异步操作队列
上面至今是 return 值
,直接调用 下一下 then
就OK了。
但如果 return Promise
,则?
Promise.resolve(111).then(function(d){ console.log(d); return Promise.resolve(d+111);//返回promise }).then(function(d2){ console.log(d2); }) // 111,222 复制代码
3. 并行问题forEach处理
当多个异步并行执行时,每个异步代码执行时间不定,所以多个异步执行结束时间无法确定(无法确定结束完时间)。
所以需要特殊处理。
//forEach 顺便无法保证 var arrs = [1,2,3,4]; var p = function(d){ return new Promise((resolve)=>{ setTimeout(()=>{ resolve(d); },Math.random()*1000);//因为异步执行时间无法确认 }); }; arrs.forEach(function(arr){ p(arr).then((d)=>{ console.log(d); }) }); 复制代码
//使用 Promise.all 来让返回有序 var arrs = [1,2,3,4]; var p = function(d){ return new Promise((resolve)=>{ setTimeout(()=>{ resolve(d); },Math.random()*1000);//因为异步执行时间无法确认 }); }; var ps = []; arrs.forEach(function(arr){ ps.push(p(arr)); }); Promise.all(ps).then(values=>{ console.log(values);//[1,2,3,4] }) 复制代码
4. 基本实现原理—实现一个简单Promise
自己手撸一个简单的 Promise
1. 版本1—极简实现
//版本1 极简实现 function Promise1(fn) { var value = null, callbacks = []; //callbacks为数组,因为可能同时有很多个回调 this.then = function (onFulfilled) { callbacks.push(onFulfilled); return this;//支持链式调用 Promise.then().then }; function resolve(value) { callbacks.forEach(function (callback) { callback(value); }); } fn(resolve); } //Test 对上面实现,写一个简单的测试 new Promise1(function(resolve){ setTimeout(function(){ resolve(1); },100); }).then(function(d){ console.log(d); }) //1 复制代码
2. 版本2—加入延时机制
//上面版本1 可能导致问题 //在then注册回调之前,resolve就已经执行了 new Promise1(function(resolve){ console.log(0) resolve(1); }).then(function(d){ console.log(d); }) // 1 不会打印 复制代码
//版本2 解决 function Promise1(fn) { var value = null, callbacks = []; //callbacks为数组,因为可能同时有很多个回调 this.then = function (onFulfilled) { callbacks.push(onFulfilled); return this;//支持链式调用 Promise.then().then }; function resolve(value) { setTimeout(function(){ callbacks.forEach(function (callback) { callback(value); }),0}); } fn(resolve); } 复制代码
3. 版本3—状态
Promise
有三种状态 pending
、 fulfilled
、 rejected
,且状态变化时单向的。
具体细节就是 在 then
, resolve
中加状态判断,具体代码略
4. Promises/A+
具体 Promise
实现有一套官方规范,具体参见Promises/A+
5. finnaly 实现
Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); }; 复制代码
6. 异常处理
异常分类:
try-catch
1. Promise 异常处理基本套路
基本处理异常中,有两种方案 then(undefined, func)
与 catch()
但 then(undefined, func)
与 catch()
不同 ,具体参见代码 方案3
//方案1 使用 Promise.prototype.catch()来catch const promise1 = new Promise((resolve, reject) => { reject('no')// }) promise1.then(result => { console.log(result) // 永远不会执行 }).catch(error => { console.log(error) // no }) 复制代码
//方案2 使用 Promise.prototype.then()中第二个参数 来处理 const promise1 = new Promise((resolve, reject) => { reject('no')// }) promise1.then(result => { console.log(result) // 永远不会执行 },error => { console.log(error) // no }) 复制代码
//方案2 (方案1 方案2 对比) var promise2 = new Promise((resolve, reject) => { resolve('yes')// }) promise2.then(result => { throw new Error('then'); console.log(result) },error => { console.log('1111',error) // no }).catch(error=>{ console.log('2222',error)// 最终 err在此处被捕获,而不是 then 中 }) 复制代码
2. 异常不同分类
Promise可能遇到的异常种类
//1.异常 reject() const promise1 = new Promise((resolve, reject) => { reject('no')// }) promise1.then(result => { console.log(result) // 永远不会执行 }).catch(error => { console.log(error) // no }) 复制代码
//2.异常 显示throw const promise1 = new Promise((resolve, reject) => { throw Error('no') }) promise1.then(result => { console.log(result) // 永远不会执行 }).catch(error => { console.log(error) // }) 复制代码
//3.执行异常 const promise1 = new Promise((resolve, reject) => { aaaa; }) promise1.then(result => { console.log(result) // 永远不会执行 }).catch(error => { console.log(error) // }) 复制代码
3. 异常链式调用
asyncThing1().then(function() { return asyncThing2(); }).then(function() { return asyncThing3(); }).catch(function(err) { return asyncRecovery1(); }).then(function() { return asyncThing4(); }, function(err) { return asyncRecovery2(); }).catch(function(err) { console.log("Don't worry about it"); }).then(function() { console.log("All done!"); }) 复制代码
上述代码的流程图形式:
// promise链式调用,catch住异常后,后面就不会处理异常了 Promise.reject().then(()=>{ console.log(2222); },(err)=>{ console.log(333,err) return err}) .catch((err)=>{ console.log(1111,err); }) //333 undefined ,没有打印 1111 复制代码
//如果 在链式调用中,then 第二个参数 catch住了异常,没有return Promise.reject()则后续链式调用返回rosolve状态pormise Promise.reject() .then(()=>{ console.log(111); },(err)=>{ console.log(111,err) //reject return err; }).then((data)=>{ console.log(222,data);//resolve 执行 },(err)=>{ console.log(222,err); //未执行 }) //4444 没有执行 1111 复制代码
4. 异常丢失
很多情况下,promise无法捕获异常
场景1macrotask 队列中抛出异常:
//场景1 //永远不要在 macrotask 队列中抛出异常,因为 macrotask 队列脱离了运行上下文环境,异常无法被当前作用域捕获。 function fetch(callback) { return new Promise((resolve, reject) => { setTimeout(() => { throw Error('用户不存在') }) }) } fetch().then(result => { console.log('请求处理', result) // 永远不会执行 }).catch(error => { console.log('请求处理异常', error) // 永远不会执行 }) // 程序崩溃 // Uncaught Error: 用户不存在 /* 参考 作者:黄子毅 链接:https://www.jianshu.com/p/78dfb38ac3d7 來源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。 */ 复制代码
//解决场景1 怎么解决,因为setTimeout 是macrotask任务,执行上下文完全不同 /** 如何解决? 调用reject */ function fetch() { return new Promise((resolve, reject) => { setTimeout(() => { reject('收敛一些') }) }) } fetch().then((resolve, reject) => { console.log('resolve'); }).catch(error => { console.log('捕获异常', error) // 捕获异常 收敛一些 }) 复制代码
场景二Promise 状态只能改变一次
//异常丢失 const promise2 = new Promise((resolve, reject) => { reject('no') console.log('reject after') throw Error('no') //异常丢失 }) promise1.then(result => { console.log(result) // 永远不会执行 }).catch(error => { console.log('err',error) // no }).catch(error => { console.log('err2',error) // 也无法捕获异常 }) 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- TiKV 成功晋级 CNCF 孵化项目
- 来自Java高级架构师的晋级心得
- 一位资深Java架构师的晋级心得
- 想晋级高级工程师只知道表面是不够的!Git内部原理介绍
- BCTF正式成为DEF CON CTF外卡赛 冠军战队直接晋级全球总决赛
- 吃透 MQ
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Docker——容器与容器云
浙江大学SEL实验室 / 人民邮电出版社 / 2015-9-1 / 89.00元
本书从实践者的角度,在讲解Docker高级实践技巧的同时,深入到源代码层次,为读者梳理出Docker容器技术和基于Docker的容器云技术(如Kubernetes)的实现方法和设计思路,帮助读者理解如何在实际场景中利用Docker解决问题并启发新的思考。全书包括两部分,第一部分深入解读Docker容器技术,包括Docker入门、架构总览、Docker容器核心原理解读,以及Docker高级实践技巧;......一起来看看 《Docker——容器与容器云》 这本书的介绍吧!