JavaScript异步编程

2019-10-09  本文已影响0人  学的会的前端

什么是异步

所谓"异步",简单说就是一个任务分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。

ES5的异步编程方法

回调函数

 readflie(/etc,function(err,data){
        if (err) throw err;
        console.log(data);
    })

Promise 对象

var readFile = require('fs-readfile-promise');

readFile(fileA)
.then(function(data){
  console.log(data.toString());
})
.then(function(){
  return readFile(fileB);
})
.then(function(data){
  console.log(data.toString());
})
.catch(function(err) {
  console.log(err);
});

协程

function asnycJob() {
  // ...其他代码
  var f = yield readFile(fileA);
  // ...其他代码
}
//上面代码的函数 asyncJob 是一个协程,它的奥妙就在其中的 yield 命令。它表示执行到此处,执行权将交给其他协程。也就是说,yield命令是异步两个阶段的分界线。

ES6的异步编程方法

Generator函数

 function *gen(x){ 
        var y = yield x + 1
        return y
    }

运行结果截图


捕获.PNG
  1. 暂停执行和恢复执行
  2. 函数体内外的数据交换
  3. 错误处理机制。
  1. next 方法返回值的 value 属性,是 Generator 函数向外输出数据;next 方法还可以接受参数,这是向 Generator 函数体内输入数据。
  2. Generator 函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。
    function *gen(x){
        try{
            var y = yield x + 1
            return y
        }catch(e){
            console.log(e)
        }
    }
捕获.PNG

Thunk 函数

function f(m){
        return m * 5
    }
f(x + 4)


 //thunk函数
var thunk = function(){
     return x + 4
}
function f(thunk){
    return thunk() * 5
}
   readFile(ileName,callback);
    
    var readFileThunk = Thunk(fileName)
    readFileThunk(callback)
    
    
    var Thunk = functin(fileName){
        return function(callback){
            return readFile(fileName, callback);
        }
    }

function run(fn) {
  var gen = fn();

  function next(err, data) {
    var result = gen.next(data);
    if (result.done) return;
    result.value(next);
  }

  next();
}

run(gen);

co 函数库

var gen = function* (){
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

co 函数库可以让你不用编写 Generator 函数的执行器。

var co = require('co');
co(gen);

上面代码中,Generator 函数只要传入 co 函数,就会自动执行。

两种方法可以做到这一点。
(1)回调函数。将异步操作包装成 Thunk 函数,在回调函数里面交回执行权。
(2)Promise 对象。将异步操作包装成 Promise 对象,用 then 方法交回执行权。

co 函数库其实就是将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个库。使用 co 的前提条件是,Generator 函数的 yield 命令后面,只能是 Thunk 函数或 Promise 对象。

    function co(gen){
        var ctx = this
        return new Promise(function(resolve,reject){
            if(typeof gen === 'function') gen = gen.call(ctx)
            if(!gen || typeof gen.next !== 'function') return resolve(gen)
            onFulfilled()
            function onFulfilled(res){
                var ret
                try{
                    ret = gen.next(res)
                }catch(e){
                    retrun reject(e)
                }
                next(ret)
            }
        })
    }
    function next(ret) {
        if(ret.done) return resolve(ret.value)
        var value = toPromise.call(ctx,ret.value)
        if(value && isPromise(value)) return value.then(onFulfilled,onRejected)
        return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
            + 'but the following object was passed: "' + String(ret.value) + '"'));
        }
    }

// 数组的写法
co(function* () {
  var res = yield [
    Promise.resolve(1),
    Promise.resolve(2)
  ];
  console.log(res); 
}).catch(onerror);

// 对象的写法
co(function* () {
  var res = yield {
    1: Promise.resolve(1),
    2: Promise.resolve(2),
  };
  console.log(res); 
}).catch(onerror);

async 函数

var asyncReadFile = async function (){
  var f1 = await readFile('/etc/fstab');
  var f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};
  1. 内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。
var result = asyncReadFile();
  1. 更好的语义。 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。
  2. 更广的适用性。 co 函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。

async function fn(args){
  // ...
}

// 等同于

function fn(args){ 
  return spawn(function*() {
    //spawn 函数为自动执行器 ...
  }); 
}
  1. await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中。
  2. await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。
  3. 如果确实希望多个请求并发执行,可以使用 Promise.all 方法。
上一篇下一篇

猜你喜欢

热点阅读