Javascript异步编程2

155 阅读4分钟

深入了解Promise

  • 规范-Promises/A+ 术语
    • promise 一个有then方法的对象或函数,行为符合本规范;
    • thenable 一个定义了then方法的对象或函数;
    • 值,value 任何Javascript的合法值;
    • 异常,exception throw语句抛出的值;
    • 拒绝原因,reason 一个标示promise被拒绝原因的值;
  • promise状态
    • pending
    • fulfilled
    • rejected

//解析代码如下
function resolve(promise,x){
    if (x === promise) {
        return reject(promise,new TypeError('cannot be the same'));
    }
    if(isPromise(x)){
        if(x.state === 'pending'){
            return x.then(()=>{
                resolve(promise,x.value);
            },()=>{
                resolve(promise,x.value);
            });
        }
        if(x.state === 'fulfilled'){
            return fulfill(promise,x.value);
        }
        if(x.state === 'rejected'){
            return reject(promise,x.value);
        }
    }else if(isObject(x) || isFunction(x)){
        let then;
        try {
            then = x.then
        } catch (error) {
            return reject(promise,e);
        }
        if(isFunction(then)){
            let isCalled = false;
            try {
                then.call(x,function resolvePromise(y){
                    if(isCalled){
                        return;
                    }
                    isCalled = true;
                    resolve(promise,y);
                },function rejectPromise(r){
                    if(isCalled){
                        return;
                    }
                    isCalled = true;
                    reject(promise,r);
                });
            } catch (error) {
                if(!isCalled){
                    return reject(promise,e);
                }
            }
           
        }else{
            return fulfill(promise,x);
        }

    }else{
        return fulfill(promise,x);
    }
}
//ES6 Promise是符合A+规范的
const promise1 = Promise.resolve(1);
const promise2 = promise1.then(2);
const promise3 = promise2.then(Promise.resolve(3));
const promise4 = promise3.then(console.log);
//输出结果为1
  • 注意点
  1. then、catch返回的promise是新的promise,不是原来的promise;
  2. Promise对象的错误会"冒泡",直到被捕获为止,错误会被下一个catch语句捕获;
  • 最佳实践
  1. 不要忘记catch错误捕获;
  2. then方法中使用return;
  3. 传递函数给then方法;
  4. 不要把promise写成嵌套;
  • 题目
    3秒之后亮一次红灯,再过2秒亮一次绿灯,再过1秒亮一次黄灯,用promise实现多次交替亮灯的效果
  • 解析
  1. 多少秒后亮某个颜色的灯;
  2. 顺序亮一批灯;
  3. 循环顺序亮一批灯;
//多少秒后亮某个颜色的灯
function light(color,second){
    return new Promise(function(resolve,reject){
        setTimeout(() => {
            console.log(color);
            resolve();
        }, second * 1000);
    });
}

//顺序亮灯
function orderLights(list){
    let promise = Promise.resolve();
    list.forEach(element => {
        promise = promise.then(function(){
          return  light(element.color,element.second);
        });
    });
    //循环顺序亮灯
    promise.then(function(){
        return orderLights(list);
    });
}

orderLights([{color:'red',second:3},{color:'green',second:2},{color:'yellow',second:1}]);

效果图如下:

async函数

  • async函数
    • 一个语法糖 使异步操作更简单
    • 实现原理:Generator + 自动执行器
    • 返回值 返回值是一个promise对象
      • return的值是promise resolved时候的value
      • throw的值是promise rejected时候的reason
  • await
    • 只能出现在async函数内或最外层
    • 等待一个promise对象的值
    • await的promise状态为rejected,后续执行中断
async function f(){
    await Promise.reject('error');
    console.log(1);
    await 100;
}
f();

上面的代码什么都不会打印,执行到await Promise.reject('error');时候程序中断,不会再往后执行,如果想让后面的代码执行怎么写?

  • async函数实现原理
async function example(params){
    //
}
function example(params){
    return spawn(function*(){
        //
    });
}
function spawn(genF){
    return new Promise(function(resolve,reject){
        const gen = genF();//生成器对象
        function step(nextF){
            let next;
            try {
                next = nextF();//执行gen.next
            } catch (error) {
                return reject(e);
            }
            if(next.done){
                return resolve(next.value);
            }
            Promise.resolve(next.value).then(function(v){
                step(function(){
                    return gen.next(v);
                })
            },function(e){
                step(function(){
                    return gen.throw(e);
                })
            });
        }
        step(function(){
            return gen.next(undefined);
        })
    });
}

Generator函数

  • 迭代器
    迭代器有next方法,执行返回结果对象,结果对象是value和done

  • 可迭代协议
    1、Symbol.iterator属性
    2、内置可迭代对象 String Array Map Set等

  • 迭代器协议
    1、next方法,返回done和value

  • ES5实现迭代器

  • 生成器

  1. Generator函数是生成器,用来生成迭代器,是ES6异步编程解决方案
  2. 声明:通过function*声明
  3. 返回值:符合可迭代协议和迭代器协议的生成器对象,就是迭代器
  4. 特点:在执行时能暂停,又能从暂停处继续执行
  5. 生成器对象组成:next(param) return(param) throw(param)
  • yield
  1. 只能出现在Generator函数
  2. 用来暂停和恢复生成器函数
  • yield*生成器函数/可迭代对象
  1. 委托给其他可迭代对象
  2. 作用:复用生成器,交出控制权
  • next执行
  1. 遇yield暂停,将紧跟yield表达式的值作为返回的对象的value
  2. 没有yield,一直执行到return,将return的值作为返回的对象的value
  3. 没有return,将undefined作为返回的对象的value
  • next参数
  1. next方法可以带一个参数,该参数将会被当做上一个yield表达式的返回值
  • return(param)
  1. 给定param值终结遍历器,param可缺省,即为undefined
  • throw(param)
  1. 让生成器对象内部抛出错误

Generator函数的实现原理

协程

  1. 一个线程存在多个协程,但同时只能执行一个;
  2. Generator函数是协程在ES6的实现;
  3. Yield挂起协程(交给其他协程),next唤醒协程;
  • Generator函数应用
  1. 需求:异步读取三个文件,但是要按照顺序的打印出file1 file2 file3的内容
  2. 如果用ayncs实现的话很简单;

上面f的耦合度太高,为了解决这个问题,用Thunk函数

  • Thunk函数
  1. 求值策略 传值调用,传名调用sum(x+1,x+2)
  2. thunk函数是传名调用的实现方式之一
  3. 可以实现自动执行Generator函数
  • co模块源码分析