Promise及其他异步处理
promise主要用于异步计算,可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果。
- promise有三个状态:
1、pending[待定]初始状态
2、fulfilled[实现]操作成功
3、rejected[被否决]操作失败
当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;
promise状态一经改变,不会再变。 - Promise对象的状态改变,只有两种可能:
从pending变为fulfilled
从pending变为rejected。
document.getElementById('#start').addEventListener('click', start, false);
function start() {
// 响应事件,进行相应的操作
}
// jquery on 监听
$('#start').on('click', start)
所谓回调函数(callback),就是把任务分成两步完成,第二步单独写在一个函数里面,等到重新执行这个任务时,就直接调用这个函数。
// 比较常见的有ajax
$.ajax('http://www.wyunfei.com/', {
success (res) {
// 这里可以监听res返回的数据做回调逻辑的处理
}
})
// 或者在页面加载完毕后回调
$(function() {
// 页面结构加载完成,做回调逻辑处理
})
异步回调的问题:
- 之前处理异步是通过纯粹的回调函数的形式进行处理
- 很容易进入到回调地狱中(函数作为参数层层嵌套),剥夺了函数return的能力
- 问题可以解决,但是难以读懂,维护困难
- 稍有不慎就会踏入回调地狱 - 嵌套层次深,不好维护
promise
- promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以(闭包除外)
- 并未剥夺函数return的能力,因此无需层层传递callback,进行回调获取数据
- 代码风格,容易理解,便于维护
- 多个异步等待合并便于解决
简单示例:
new Promise(resolve => {
setTimeout(() => {
resolve('hello')
}, 2000)
}).then(val => {
console.log(val) // 参数val = 'hello'
return new Promise(resolve => {
setTimeout(() => {
resolve('world')
}, 2000)
})
}).then(val => {
console.log(val) // 参数val = 'world'
})
.then()
1、接收两个函数作为参数,分别代表fulfilled(成功)和rejected(失败)
2、.then()返回一个新的Promise实例,所以它可以链式调用
3、当前面的Promise状态改变时,.then()根据其最终状态,选择特定的状态响应函数执行
4、状态响应函数可以返回新的promise,或其他值,不返回值也可以我们可以认为它返回了一个null;
5、如果返回新的promise,那么下一级.then()会在新的promise状态改变之后执行
6、如果返回其他任何值,则会立即执行下一级.then()
错误处理
Promise会自动捕获内部异常,并交给rejected响应函数处理。
第一种:reject('错误信息').then(() => {}, () => {错误处理逻辑})
第二种:throw new Error('错误信息').catch( () => {错误处理逻辑})
推荐使用第二种方式,更加清晰好读,并且可以捕获前面所有的错误(可以捕获N个then回调错误)


Promise.all() 批量执行
Promise.all([p1, p2, p3])用于将多个promise实例,包装成一个新的Promise实例,返回的实例就是普通的promise
它接收一个数组作为参数
数组里可以是Promise对象,也可以是别的值,只有Promise会等待状态改变
当所有的子Promise都完成,该Promise完成,返回值是全部值得数组
有任何一个失败,该Promise失败,返回值是第一个失败的子Promise结果
Promise.race() 类似于Promise.all() ,区别在于它有任意一个完成就算完成。
//切菜
function cutUp(){
console.log('开始切菜。');
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('切菜完毕!');
resolve('切好的菜');
}, 1000);
});
return p;
}
//烧水
function boil(){
console.log('开始烧水。');
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('烧水完毕!');
resolve('烧好的水');
}, 1000);
});
return p;
}
Promise.all([cutUp(), boil()])
.then((result) => {
console.log('准备工作完毕');
console.log(result);
})
链接:https://www.jianshu.com/p/1b63a13c2701
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
手写一个promise
这里我们创建了一个构造函数 参数就是执行器
function Promise(exector) {
// 这里我们将value 成功时候的值 reason失败时候的值放入属性中
let that = this
this.value = undefined
this.reason = undefined
function resolve(value) {
that.value = value
}
function reject(reason) {
that.reason = reason
}
exector(resolve, reject)
}
function Promise(exector) {
// 这里我们将value 成功时候的值 reason失败时候的值放入属性中
let that = this
this.value = undefined
this.value = undefined
this.status = 'pending'
function resolve(value) {
if (that.status == 'pending') {
that.value = value
that.status = 'resolved'
}
}
function reject(reason) {
if (that.status == 'pending') {
that.reason = reason
that.status = 'resolved'
}
}
try{
exector(resolve, reject)
} catch(e) {
reject(e)
}
}
Promise.prototype.then = function(onFulfilled, onRejected) {
let that = this
if (that.status == 'resolved') {
onFulfilled(that.value)
}
if (that.status == 'rejected') {
onRejected(that.reason)
}
}
自己可运行
let promise = new Promise((resolve, reject) => {
resolve("haha");
})
promise.then(data => {
console.log(data); //输出 haha
}, err=> {
console.log(err);
})
// 多次调用
promise.then(data => {
console.log(data); //输出 haha
}, err=> {
console.log(err);
})
链接:https://www.jianshu.com/p/4b126518c26d
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
但这样写绝不是最好的,代码变得十分冗余,一堆的then。
所以,最优秀的解决方案是什么呢?是async/await
协程(coroutine),意思是多个线程相互协作,完成异步任务。
它的运行流程如下
- 协程A开始执行
- 协程A执行到一半,暂停执行,执行的权利转交给协程B。
- 一段时间后B交还执行权
- 协程A重得执行权,继续执行
function* gen(x) { var y = yield x +2; return y; } var g = gen(1); console.log( g.next()) // { value: 3, done: false } console.log( g.next()) // { value: undefined, done: true }
上面代码中,函数多了*号,用来表示这是一个Generator函数,和普通函数不一样,不同之处在于执行它不会返回结果,
返回的是指针对象g,这个指针g有个next方法,调用它会执行异步任务的第一步。
对象中有两个值,value和done,value 属性是 yield 语句后面表达式的值,表示当前阶段的值,done表示是否Generator函数是否执行完毕。
var fetch = require('node-fetch'); function* gen(){ var url = 'https://api.github.com/users/github'; var result = yield fetch(url); console.log(result.bio); } var g = gen(); var result = g.next(); result.value.then( data => return data.json) .then (data => g.next(data))
上面代码中,首先执行Generator函数,得到对象g,调用next方法,此时
result ={ value: Promise { <pending> }, done: false }
因为fetch返回的是一个Promise对象,(即value是一个Promise对象)所以要用then才能调用下一个next方法。
虽然Generator将异步操作表示得很简洁,但是管理麻烦,何时执行第一阶段,又何时执行第二阶段?
是的,这时候到Async/await出现了!
var gen = function* (){ var f1 = yield readFile('./a.txt'); var f2 = yield readFile('./b.txt'); console.log(f1.toString()); console.log(f2.toString()); }; var asyncReadFile = async function (){ var f1 = await readFile('./a.txt'); var f2 = await readFile('./b.txt'); console.log(f1.toString()); console.log(f2.toString()); };
async函数返回的是一个Promise对象,如果直接return 一个直接量,async会把这个直接量通过PromIse.resolve()封装成Promise对象。
一般来说,都认为await是在等待一个async函数完成,确切的说等待的是一个表示式,这个表达式的计算结果是Promise对象或者是其他值(没有限定是什么)
即await后面不仅可以接Promise,还可以接普通函数或者直接量。
同时,我们可以把async理解为一个运算符,用于组成表达式,表达式的结果取决于它等到的东西
- 等到非Promise对象 表达式结果为它等到的东西
- 等到Promise对象 await就会阻塞后面的代码,等待Promise对象resolve,取得resolve的值,作为表达式的结果
下面的例子,指定多少毫秒后输出一个值。
function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function asyncPrint(value, ms) { await timeout(ms); console.log(value) } asyncPrint('hello world', 50);
上面代码指定50毫秒以后,输出"hello world"。
async函数的优点
(1)内置执行器
Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。
async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。
(2) 语义化更好
async 和 await,比起星号和 yield,语义更清楚了。async 是“异步”的简写,而 await 可以认为是 async wait 的简写。所以应该很好理解 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。
(3)更广的适用性
yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
作者:Ciger
链接:https://www.jianshu.com/p/1c9e9c161612
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
浙公网安备 33010602011771号