Skip to content

Promise & async

Promise

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

实现一个 Promise

javascript
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不同,可以返回多次
javascript
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结合使用的

javascript
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值,继续执行

javascript
async function foo() {
  await console.log(121212)
  console.log(121212)
}
javascript
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);
}

测试

javascript
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

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

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

利用promise

javascript
(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方法指定下一步的操作。