JavaScript 单线程 回调函数generator

JavaScript 解析引擎是单线程(single thread),即在一个进程上,只能按顺序运行一个线程,而不能多线程运行,这就是同步调用(Synchronous Call)。这种运行模式相对简单,执行环境相对单纯。但是会出现一个问题,如果一个线程执行时间漫长执行速度缓慢,会造成浏览器无响应,导致整个页面卡在这里,其他进程也无法执行。
对于这种问题,JavaScript还有一种异步执行(Asynchronous Call),它能解决浏览器长时间等待的执行问题,避免浏览器失去响应。比较常用的异步方式是Ajax。同步和异步最大的差别在于线程流各个进程执行的顺序。举个栗子,有个任务是读取文件处理,任务的第一段是向操作系统发出请求,要求读取文件。然后继续执行其他任务,等操作系统返回文件后,再继续执行任务的第二段(处理文件)。
异步执行中必须指定回调函数,回调函数是被主进程先挂起来的代码,当主进程要执行的时候,才调用挂起的函数。

第一种回调函数

function readFile(cb){
    fs.readFile('../package.json',(err,data)=>{
        if(err) return cb(err)
        cb(null,data)
    })
}
readFile((err,data)=>{
    if(!err){
        data = JSON.parse(data)
        console.log(data.name)
    }
})

这种回调方式是当一方任务先满足条件,再回执行下一个任务。这种这行方式较简单,适合回调比较少的条件。当回调任务比较多时候,这种嵌套就会造成无限极级嵌套,代码变得非常繁琐。这种情况称为callback hell(回调地狱)。

第二阶段 Promise

Promise 比传统解决回调函数和时间更合理和强大,它不是新的语法功能,而是一种新的写法。允许将回调函数的嵌套,改成链式调用。它是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前哪个状态,任何其他操作都无法改变这个状态。
优点:避免了层层嵌套的回调函数,提供统一的接口,使得控制异步操作更简单。
缺点:一旦建立就立刻执行,无法中途取消Promise,无法监控执行到哪个阶段。

function readFileAsync(path){
    return new Promise((resolve,reject)=>{
        fs.readFile(path,(err,data)=>{
            if(err) reject(err)
            else resolve(data)
        })
    })
}
readFileAsync('../package.json')
    .then(data=>{
        data = JSON.parse(data)
        console.log(data.name)
    })
    .catch(err=>{
        console.log(err);        
    })

第三个阶段 co+ Generator Function + promisify

Generator 函数是ES6 提供的一种异步编程解决方案,Generator 函数在语法上可以理解成一个状态机,封装了多个内部状态。执行Generator 函数会返回一个遍历对象。形式上,Generator 函数是一个普通函数。

function *testGenerator(arr){
    for(let i=0;i<arr.length;i++){
        yield arr[i];
    }
}
const gen = testGenerator(['first','second','third'])
console.log('frist',gen.next().value) // first
console.log('second',gen.next().value) //second
console.log('third',gen.next().value) //third
console.log('end',gen.next().done) //true

next() 是调用遍历对象的方法,使得指针移向下一个状态。当done 属性为true 时候,表示遍历结束。yield 表达式后面的表达式只有调用next(),内部指针指向该函数 才会执行。

co 模块是著名程序员TJ 于2013年6月发布的一个小工具,用于Generator 函数的自动执行。

const co = require('co');
const util = require('util');
co(function *(){
    let data = yield util.promisify(fs.readFile)('../package.json')
    data = JSON.parse(data)
    console.log(data.name)
})

第四阶段 Async/Await Promise

ES7 引入了async/await 函数,async 函数返回一个Promise 对象,then() 添加回调函数,遇到await 先执行异步操作,等到异步操作完成,再执行函数体内其他语句。
await
The await operator is used to wait for a Promise. It can only be used inside and async function.
[return_value] = await expression
return_value: Returns the fulfilled value of the promise, or the value itself if it’s not a Promise
expression: A Promise or any value to wait for.
The await expression cause async function execution to pause until a Promise is resolved, that is fulfilled or rejected, and to resume execution of the async function after fulfillment. when resumed, the value of the await expression that of the fulfilled Promise.
If the Promise is rejected, the await expression throws the reject value.

function testAsync(x){
    return new Promise(resolve=>{
        setTimeout(()=>{
            resolve(x);
        },1000)
    });
}
async function f1(){
    let x = await testAsync(10);
    console.log(x);   //10
}
// If the value is not Promise, it converts the value to resolved Promise,and waits for it
async function f2(){
    let y = await 20;
    console.log(y); //20
}
//If the Promise is rejected,the rejected value is thrown
async function f3(){
    try{
        let z = await Promise.reject(30)
    }catch(e){
        console.log(e);//30
    }
}
const util = require('util');
const readAsync = util.promisify(fs.readFile);

async function init(){
    let data = await readAsync('../package.json')
    data = JSON.parse(data)
    console.log(data.name)
}
init();

参考:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/await

猜你喜欢

转载自blog.csdn.net/yana_loo/article/details/80705840
今日推荐