Promise晋级,须要的所有都在这javascript
主要内容:java
- promise基本实现原理
- promise 使用中难点(链式调用,API基本上返回都是一个新Promise,及参数传递)
- promise 对异常处理
- promise 简单实现及规范
- es6 对promise更进一步掌握Async晋级——彻底吃透
参考:es6
30分钟,让你完全明白Promise原理web
阮一峰ES6入门ajax
JavaScript Promise:简介json
基本的promise使用,读本文须要了解基本的Promise
使用。segmentfault
查兼容性 基本上 主流浏览器支持没有问题。promise
IE不兼容 问题,本文不予以处理,出门左转,找谷哥。具体查看 babel,或者 本身实现一个Promise浏览器
//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();
});
}
复制代码
Promise API 分为 :MDN
静态方法
prototype
上方法
Promise.prototype.then()
来分析
首先来看看 `Promise.prototype.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)
})
复制代码
链式调用
- 核心就是 then catch 等方法返回一个Promise
- 链式 调用数据传递(注意)
简单例子
//正常状态
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。
复制代码
上面至今是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
复制代码
当多个异步并行执行时,每一个异步代码执行时间不定,因此多个异步执行结束时间没法肯定(没法肯定结束完时间)。
因此须要特殊处理。
//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]
})
复制代码
本身手撸一个简单的Promise
//版本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
复制代码
//上面版本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);
}
复制代码
Promise
有三种状态pending
、fulfilled
、rejected
,且状态变化时单向的。
具体细节就是 在then
,resolve
中加状态判断,具体代码略
具体 Promise
实现有一套官方规范,具体参见Promises/A+
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 })
);
};
复制代码
异常分类:
- 同步异常
- 异步异常 没法
try-catch
获得- 多层Promise嵌套,获异常取具体的一个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 中
})
复制代码
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) //
})
复制代码
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
复制代码
不少状况下,promise没法捕获异常
场景1 macrotask 队列中抛出异常:
//场景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) // 也没法捕获异常
})
复制代码
async是 Promise 更高一层的封装,具体参见深刻浅出Async