Promise & async

[[toc]]

Promise

  • 主要用于异步计算
  • 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
  • 可以在对象之间传递和操作promise,帮助我们处理队列

实现一个 Promise

let  resolvePromise = (promise2,x,resolve,reject) => {
// 判断x的类型 来处理promise2是成功还是失败
// 所有的promise都遵循这个规范,不同的人写的promise可能会混用
// 尽可能考虑的周全 要考虑别人promise可能出错的情况
if(promise2 === x){
return reject(new TypeError('循环引用'))
}
// 判断x是不是一个promise ,这个x 可能不是自己的promise 所以为了安全 需要在进行校验,放置调一起用成功和失败
if(typeof x === 'function' || (typeof x === 'object' && x !== null)){
// 尝试取当前x的then方法, 这个then方法可能别人定义的时候 用的Object.defineProperty
let called;
try{
let then = x.then; // 如果取then方法出错 那就用错误拒绝promise2
if(typeof then === 'function'){ // 我就认为他是一个promise
then.call(x,y=>{ // 让当前的promise执行 ,不用多次取then方法了
// y 有可能还是一个promise , 继续调用resolvePromise方法,直到解析出一个常量为止,最终把常量传递下去
if(called) return; // 放置此方法多次被调用
called = true;
resolvePromise(promise2,y,resolve,reject);
},r=>{
if(called) return;
called = true;
reject(r); // 让当前的promise变成失败态即可
})
}else{
// x就是一个普通的对象 并没有then方法
resolve(x);
}
}catch(e){
if(called) return;
called = true;
reject(e);
}
}else{
// x肯定一个常量
resolve(x);
}
}

class Promise{
constructor(executor){
this.status = 'pending';
this.value;
this.reason;
this.resolveCallbacks = []; // 当then是pending 我希望吧成功的方法都放到数组中
this.rejectCallbacks = [];
let resolve = (value)=>{
// 如果是promise就调用这个promise的then方法
if(value instanceof Promise){
// 不停的解析 等待着解析出一个常量 传递给下面
return value.then(resolve,reject);
}
if(this.status == 'pending'){
this.status = 'fulfilled';
this.value = value;
this.resolveCallbacks.forEach(fn=>fn()); // 发布
}
}
let reject = (reason)=>{
if(this.status === 'pending'){
this.status = 'rejected';
this.reason = reason;
this.rejectCallbacks.forEach(fn=>fn())
}
}
try{
executor(resolve,reject);
}catch(e){
reject(e);
}
}
then(onfulfilled,onrejected){ // onfulfilled,onrejected 是两个可选参数
onfulfilled = typeof onfulfilled === 'function'?onfulfilled:val=>val;
onrejected = typeof onrejected === 'function'?onrejected:r=>{throw r}
let promise2;
promise2 = new Promise((resolve,reject)=>{
if(this.status === 'fulfilled'){
setTimeout(()=>{
try{
let x = onfulfilled(this.value);
// x是普通值还是promise 如果是普通值 直接调用promise2的resolve
// 如果是promise 那应该让x这个promise执行 x.then
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
},0);
}
if(this.status === 'rejected'){
setTimeout(()=>{
try{
let x = onrejected(this.reason);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
},0)
}
if(this.status === 'pending'){
this.resolveCallbacks.push(()=>{
setTimeout(()=>{
try{
let x = onfulfilled(this.value);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
},0)
});
this.rejectCallbacks.push(()=>{
setTimeout(()=>{
try{
let x = onrejected(this.reason);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
},0)
})
}
});
return promise2;
}
catch(rejectFunc){// catch的实现
return this.then(null,rejectFunc);
}
}
// 暴露一个方法这个方法需要返回一个对象 对象上需要有 promise resolve reject 三个属性
// 减少嵌套
Promise.defer = Promise.deferred = function(){
let dfd = {}
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
// 产生成功的promise
Promise.resolve = function(value){
return new Promise((resolve,reject)=>{
resolve(value);
})
}
// 产生一个失败的promise
Promise.reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason);
})
}
Promise.all = function(values){
return new Promise((resolve,reject)=>{
let results = []; // 结果数组
let i = 0;
let processData = (value,index)=>{
results[index] = value;
// 当成功的个数 和 当前的参数个数相等就把结果抛出去
if(++i === values.length){
resolve(results);
}
}
for(let i = 0 ; i< values.length;i++){
let current = values[i]; // 拿到数组中每一项
// 判断是不是一个promise
if((typeof current === 'object' && current !==null)|| typeof current == 'function'){
// 如果是promise
if(typeof current.then == 'function'){
// 就调用这个promise的then方法,把结果和索引对应上,如果任何一个失败了返回的proimise就是一个失败的promise
current.then(y=>{
processData(y,i);
},reject);
}else{
processData(current,i);
}
}else{
processData(current,i);
}
}
});
}
// race的原理
Promise.race = function(values){
return new Promise((resolve,reject)=>{
for(let i = 0 ; i< values.length;i++){
let current = values[i];
if((typeof current === 'object' && current !==null)|| typeof current == 'function'){
let then = current.then;
if(typeof then == 'function'){ // 比较哪个promise比较快,谁快用快
then.call(current,resolve,reject)
}else{
resolve(current);
}
}else{
resolve(current);
}
}
});
}

Generator

  • generator也是为了解决地狱回调问题的,和promise一样都是为了实现异步编程,本质还是各种回调;
  • generator为es6中新定义的数据类型,这种数据类型和函数很像,每个函数只能返回一个结果,即只能return一次,
    如果在某些函数中没有看到return,其实质在函数结尾是存在一个隐藏的return undefined 的,而generator不同,可以返回多次
function* gen(){
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return "结束";
}

let g = gen();
let i = 0;
let timer = setInterval(() => {//每间隔500ms执行一次g.next(),执行7次,并在控制台打印
i++;
console.log(g.next()); //执行gen函数
if(i>7){
clearInterval(timer);
}
}, 500);

try{
g.throw(new Error('test1'))
}catch(e){
console.log(e)
}

上述例子 可以看出generator 遇到yleld就会暂停,只有当调用generator.next()才会向下执行,
调用这个方法会返回{value: x, done: true/false},这个对象中value是yield的返回值,
done表示generator是否执行结束,只有当执行到return时,这个对象中的done才会变成true,说明执行结束

async/await

原理就是利用 generator(生成器)分割代码片段。然后我们使用一个函数让其自迭代,每一个yield 用 promise 包裹起来。执行下一步的时机由 promise 来控制

而且相较于Promise,async的优越性就是把每次异步返回的结果从then中拿到最外层的方法中,不需要链式调用,只要用同步的写法就可以了。
更加直观而且,更适合处理并发调用的问题。但是async必须以一个Promise对象开始 ,所以async通常是和Promise结合使用的

function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments;
// 将返回值promise化
return new Promise(function(resolve, reject) {
// 获取迭代器实例
var gen = fn.apply(self, args);
// 执行下一步
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
}
// 抛出异常
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
}
// 第一次触发
_next(undefined);
});
};
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
// 迭代器完成
resolve(value);
} else {
// -- 这行代码就是精髓 --
// 将所有值promise化
// 比如 yield 1
// const a = Promise.resolve(1) a 是一个 promise
// const b = Promise.resolve(a) b 是一个 promise
// 可以做到统一 promise 输出
// 当 promise 执行完之后再执行下一步
// 递归调用 next 函数,直到 done == true
Promise.resolve(value).then(_next, _throw);
}
}

await
每次遇到await都会中断此次进程,然后去执行外面的同步代码,然后再进来,根据上次保存的next值,继续执行

async function foo() {
await console.log(121212)
console.log(121212)
}
function _foo() {
_foo = _asyncToGenerator(
regeneratorRuntime.mark(function _callee() {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return console.log(121212);
case 2:
console.log(121212);
case "end":
return _context.stop();
}
}
}, _callee);
}));
return _foo.apply(this, arguments);
}

测试

const asyncFunc = _asyncToGenerator(function*() {
const e = yield new Promise(resolve => {
setTimeout(() => {
resolve('e');
}, 1000);
});
const a = yield Promise.resolve('a');
const d = yield 'd';
const b = yield Promise.resolve('b');
const c = yield Promise.resolve('c');
return [a, b, c, d, e];
});

asyncFunc().then(res => {
console.log(res); // ['a', 'b', 'c', 'd', 'e']
});

总的来说,async和generator函数主要就是为了解决异步的并发调用使用的 ,直接将参数从then里取出来,相比promise的链式调用,传参更加方便,异步顺序更加清晰

捕获错误

try catch

(async () => {
try {
const data = await fn()
} catch(err) {
console.log('err is ->', err)
}
})()

单个还好,多个await就显得麻烦了

利用promise

(async () => {
const [err, data] = await fn().then(data => [null, data] ).catch(err => [err, null])
})()

// 抽离成公共方法
const awaitWrap = (promise) => {
return promise
.then(data => [null, data])
.catch(err => [err, null])
}
const [err, data] = await awaitWrap(fn())

async函数Generator函数的区别

1.内置执行器。

Generator 函数的执行必须靠执行器,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。如果你是从上面顺着看下来的,这里的执行器就是Generator和Iterator的yield和next机制,不用怀疑!

2.更好的语义。

async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

3.正常情况下,await命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve的 Promise 对象。

4.返回值是 Promise。

async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。