const promise = new Promise(function(resolve,reject){
// 这里用于“兑现”承诺
resolve(100) // 承诺达成
// reject(new Error('promise error')) // 承诺失败
// 调用resolve或者reject修改状态后就不能再改变状态了
})
promise.then(function(value){
// resolve回调
console.log('resolved',value);
},function(error){
// reject回调
console.log("rejected",error);
})
function ajax(url){
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest()
xhr.open('GET',url)
xhr.responseType = 'json'
xhr.onload = function(){
if(this.status == 200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
// 手动在根目录下创建一个api文件夹,里面建一个user.json文件
const promise = ajax('/api/user.json')
const promise2 = promise.then( // then方法的作用就是为Promise对象去添加状态明确后的回调函数 重点是它的内部也会返回一个 Promise 对象
function onFulfilld(value){
// onFulfilld 成功过后的回调
console.log("onFulfilld",value);
},
function onRejected(error){
// onRejected 失败过后的回调
console.log("onRejected",error);
}
)
// 每一个then方法都是在为上一个 Promise 对象添加状态明确后的回调
const promise3 = ajax('/api/user.json')
.then(function(value){
console.log('111');
return ajax('/api/user.json') // 我们也可以手动返回一个 Promise 对象
}).then(function(value){
console.log('222',value);
return "foo"
}).then(function(value){
console.log('333',value);
}).then(function(value){
console.log('444');
})
// 总结:
// Promise 对象的 then 方法回返回一个全新的 Promise 对象
// 后面的 then 方法就是在为上一个 then 返回的 Promise 注册回调
// 前面 then 方法中回调函数的返回值会作为后面 then 方法回调函数的参数
// 如果回调中返回的是 Promise ,那后面的 then 方法的回调会等待它的结束
ajax('/api/user.json')
.then(function(res){
console.log(res);
return ajax('/error-url')
})
.catch(function(err){
// catch 是为上一个then返回的 Promise 注册异常回调
console.log(err);
})
// Promise 上任何一个异常都会一直向后传递,直至被捕获
// catch 方式更像是给整个 Peomise 链条注册的回调,所以更通用些
Promise.resolve()会返回一个状态为 fulfilled 的 Promise对象
// ============================== Promise.resolve() ==============================
// 这个 字符串‘foo’ 就会作为返回的 promise对象所返回的值
Promise.resolve('foo')
.then(function(val){
console.log(val);
})
// 相当于
new Promise(function(resolve,reject){
resolve('foo')
})
// 如果 Promise.resolve()接收到的是另一个 Promise对象,则会原样返回这个Promise对象
const promise = ajax('/api/user.json')
const promise2 = Promise.resolve(promise)
console.log(promise === promise2);// true
// ============================== Promise.reject() ==============================
Promise.reject(new Error('rejected'))
.catch(function(err){
console.log(err);
})
Promise.reject('anything')
.catch(function(err){
console.log(err);
})
promise.all()可以将多个 Promise 组合成一个 Promise,统一去管理
接收的是一个数组,数组中的每一个元素都是一个 Promise对象
会返回一个全新的 Promise对象
当内部所有的 Promise全部完成之后,这个全新的 Promise才会完成
const promise = Promise.all([
ajax('/api/user.json'),
ajax('/api/list.json')
])
// 新 Promise中的Promise全部成功了,才会以成功结束,有一个失败了,就会以失败结束
promise.then(function(val){
// 返回的是一个数组,数组中的每一项是对应的 Promise的结果
console.log(val);
}).catch(function(err){
console.log(err);
})
promise.race() 只会等待第一个结束的任务,只要有一个任务结束了,它也就结束了
返回结果以第一个结束的任务结果为准
const request = ajax('/api/user.json')
const timeout = new Promise(function(resolve,reject){
setTimeout(function(){
reject('失败了')
},500)
})
Promise.race([
request,
timeout
]).then(val=>{
console.log(val);
}).catch(err=>{
console.log(err);
})
console.log('global start');
setTimeout(()=>{
console.log('setTimeout');
},0)
Promise.resolve()
.then(()=>{
console.log('promise');
})
.then(()=>{
console.log('promise 2');
})
.then(()=>{
console.log('promise 3');
})
.then(()=>{
console.log('promise 4');
})
console.log('global end');
// 打印结果如下:
// global start
// global end
// promise
// promise 2
// promise 3
// promise 4
// setTimeout
这涉及到了宏任务与微任务的概念。
宏任务:可以理解为每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。 浏览器为了让 JS 内部宏任务 与 DOM 操作能够有序的执行,会在一个宏任务执行结束后,在下一个宏任务执行开始前,对页面进行重新渲染。
宏任务包含:script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、MessageChannel 等
微任务:可以理解是在当前任务执行结束后需要立即执行的任务。也就是说,在当前任务后,在渲染之前,执行清空微任务。
所以它的响应速度相比宏任务会更快,因为无需等待 UI 渲染。
微任务包含:Promise.then、MutaionObserver、process.nextTick(Node.js 环境)等
ES6 新引入了 Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案
function * foo2(){
console.log("foo2");
// yield并不会像return 一样立即结束函数的执行,他只是暂停这个函数的执行,知道下一次去调用生成器的next()方法时,会从yield这个位置继续往下执行
yield 'foo'
yield 'foo2'
}
const generator2 = foo2()
console.log(generator2.next()); // { value:'foo' , done:false} done表示这个生成器是否已经全部执行完毕
console.log(generator2.next()); // {value:'foo2' , done:true}
console.log(generator2.next()); // {value:undefined , done:true}
// ====================================== 分割线 ====================================
// 如果传入了参数的话,这个参数会作为 yield语句的返回值
function * foo3(){
// yield并不会像return 一样立即结束函数的执行,他只是暂停这个函数的执行,知道下一次去调用生成器的next()方法时,会从yield这个位置继续往下执行
const res = yield 'foo'
console.log(res);
const res2 = yield 'foo2'
console.log(res2);
}
const generator3 = foo3()
generator3.next()
// 需要在第一次执行到 yield 关键字之后 ,再次执行时传入参数
// 这个参数会作为 yield的返回值,res就会接收到这个返回值
// 传参的同时,函数继续执行,到下一次yield暂停
console.log(generator3.next("baibai"));
console.log(generator3.next("嘿嘿"));
// ====================================== 分割线 ====================================
// 使用try catch监听异常
function * foo4(){
try{
const res = yield 'foo'
console.log(res);
}catch(e){
console.log(e);
}
}
const generator4 = foo4()
const result = generator4.next()
generator4.next("cool")
generator4.throw(new Error('Generator error'))
function ajax(url){
return new Promise(function(resolve,reject){
// throw new Error()
var xhr = new XMLHttpRequest()
xhr.open('GET',url)
xhr.responseType = 'json'
xhr.onload = function(){
if(this.status == 200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
function * main(){
try{
const users = yield ajax('/api/user.json')
console.log(users);
const list = yield ajax('/api/list.json')
console.log(list);
} catch(err){
console.log(err);
}
}
const result = g.next() // 这里会的到一个对象,对象的 value值是一个 Promise对象
result.value.then(data =>{
// 将data作为参数,data会作为yield的返回值,赋值给users,同时函数继续往下执行,result2接收的值为 {value:ajax('/api/list.js') , done:false}
const result2 = g.next(data)
if(result2.done) return
result2.value.then(data=>{
const result3 = g.next(data)
if(result2.done) return
result3.value.then(data=>{
g.next(data)
}
})
})
优化代码
function handleResult(result){
if(result.done) return;
result.value.then(data=>{
// 再次执行next,并将执行结果作为参数再次调用 handlerResult方法
handleResult(g.next(data))
},err=>{
g.throw(err)
})
}
handleResult(g.next())
封装
function co(generator){
const g = generator()
function handleResult(result){
if(result.done) return;
result.value.then(data=>{
// 再次执行next,并将执行结果作为参数再次调用 handlerResult方法
handleResult(g.next(data))
},err=>{
g.throw(err)
})
}
handleResult(g.next())
}
co(main)
也有已经封装好的更完善的库,例如:https://github.com/tj/co
// 结合上面的Generator和Promise的联合使用来看,async和await的使用只是改动了部分内容
async function main(){
try{
const users = await ajax('/api/user.json')
console.log("第一次打印",users);
const list = await ajax('/api/list.json')
console.log("第二次打印",list);
}catch(err){
console.log(err);
}
}
main()