从js来聊聊异步编程

文章的目标

揭开go的 gorouter,c#的 async/await等 运用同步的写法写异步代码的神奇面纱 , 证实其本质就是一个语法糖

为何运用js来说异步编程

由于js能够经由过程编程言语本身的语法特征,完成async/await语法

js异步最底层写法promise

const promise = new Promise(function(resolve, reject) {
  xxxxx.异步IO操纵((res)=>{
      if(res胜利){
          resolve(res)
      }else{
          reject(res)
      }
  })
});

promise相差的回调函数有肯定的要求

  • resolve函数的作用是,将Promise对象的状况从“未完成”变成“胜利”(即从 pending 变成 resolved),在异步操纵胜利时挪用,并将异步操纵的结果,作为参数通报出去
  • reject函数的作用是,将Promise对象的状况从“未完成”变成“失利”(即从 pending 变成 rejected),在异步操纵失利时挪用,并将异步操纵报出的毛病,作为参数通报出去。

Promise实例天生今后,能够用then要领离别指定resolved状况和rejected状况的回调函数(处置惩罚返回的结果)。

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

引伸-注重: promise对象在js中非常特别,比方下面的例子

const p1 = new Promise(function (resolve, reject) {
  setTimeout(() => reject(new Error('fail')), 3000)
})

const p2 = new Promise(function (resolve, reject) {
  setTimeout(() => resolve(p1), 1000)
})
p2
  .then(result => console.log(result))
  .catch(error => console.log(error))

这个的结果是failt 由于 p2中resolve返回一个promise对象,这个操纵将会致使p2的状况升级成p1的状况(规范)

promise的then链式写法

promise then要领将会返回一个promise,所以js支撑链式异步

var getJSON = function (url, callback) {
    var promise = new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open("GET", url);
        client.onreadystatechange = handler;//readyState属性的值由一个值变成另一个值时,都邑触发readystatechange事宜
        client.responseType = "json";
        client.setRequestHeader("Accept", "application/json");
        client.send();

        function handler() {
            if (this.readyState !== 4) {
                return;
            }
            if (this.status === 200) {
                callback(this.response);
                resolve(this.response);
            } else {
                reject(new Error(this.statusText))
            }
        };
    });
    return promise;
};
getJSON("./e2e-tests/get.json", function (resp) {
    console.log("get:" + resp.name);
}).then(function (json) {
    getJSON("./e2e-tests/get2.json", function (resp) {
        console.log("get2:" + resp.name);
    })
}).catch(function (error) {
    console.log("error1:" + error);
});

promise 非常捕捉

p.then((val) => console.log('fulfilled:', val))
  .catch((err) => console.log('rejected', err));

// 等同于
p.then((val) => console.log('fulfilled:', val))
  .then(null, (err) => console.log("rejected:", err));

这个非常捕捉和java雷同,捕捉在eventLoop中发生的非常

注重一点这个非常和java的try catch是差别的,假如发生了非常将不会在主线程中显示出来

promise的finally

这个和java的非常系统雷同,finally 无关状况,末了都邑实行

Promise.resolve(2).finally(() => {})

越发轻易的编写异步运用Promise.resolve(xxx)

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

注重: promise异步化结果只能在回调函数中取得,假如异步的操纵太多,将会调至挪用链路太长

怎样处理js的promise异步编程的题目?

promise 写法有什么题目? —- 挪用链路太长

比方: 运用promise 完成 异步ajax要求

var getJSON = function (url, callback) {
    var promise = new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open("GET", url);
        client.onreadystatechange = handler;//readyState属性的值由一个值变成另一个值时,都邑触发readystatechange事宜
        client.responseType = "json";
        client.setRequestHeader("Accept", "application/json");
        client.send();
        function handler() {
            if (this.readyState !== 4) {
                return;
            }
            if (this.status === 200) {
                callback(this.response);
                resolve(this.response);
            } else {
                reject(new Error(this.statusText))
            }
        };
    });
    return promise;
};
getJSON("./e2e-tests/get.json", function (resp) {
    console.log("get:" + resp.name);
}).then(function (json) {
    getJSON("./e2e-tests/get2.json", function (resp) {
        console.log("get2:" + resp.name);
    })
}).catch(function (error) {
    console.log("error1:" + error);
});

挪用链太长,不断的promise挪用

js怎样处理回调地狱—同步要领写异步

处理要领 运用js的协程 –Generator

generator:js的特别语法,运用yield 关键字将函数分块了,然后能够运用遍历器手动掌握实行

例子:

function * gen(){
    let a= 123;
    let b = yield a;
    let c = yield a+b;
    return a+b+c;
}

let start = gen();

console.log(start.next());
console.log(start.next(2));
console.log(start.next(3));

本质上是函数分片

js在每次yield的时刻都邑取得当前位置的表达式,然后再手动的嵌入就能够完成分片掌握的结果了

怎样用generator完成异步化呢 — yield合营promise完成异步

看一下这个要领

function* asyncFn(value) {
    let a = yield promiseOne(value);
    let b = yield promiseTwo(a);
    return a + b;
}

想让他能异步实行,只需能让前一个promise的结果是下一个promise的输入就能够了

这里有两种写法

写法一

递归方程: f(终究结果) = f(到现在的结果)+f(接下来实行的结果)

function promiseOne(xxx) {
    return new Promise((res, rej) => {
        res(xxx + 1);
    })
}
function promiseTwo(xxx) {
    return new Promise((res, rej) => {
        res(xxx + 1);
    })
}
function* asyncFn(value) {
    let a = yield promiseOne(value);
    let b = yield promiseTwo(a);
    return a + b;
}
function runAsync(fn,value) {
    let item = fn.next(value);
    return new Promise((res, rej) => {
        if (!item.done) {
            if (item.value instanceof Promise) {
                item.value.then((re)=>{
                    runAsync(fn,re).then(res);
                })
            } else {
                runAsync(fn,fn.valueOf()).then(res);
            }
        } else {
            res(item.value);//这个res要领实际上是一切人的res要领
        }
    })
}
runAsync(asyncFn(12)).then(res=>{
    console.log(res);
});

co 工具包的写法

function run (gen) {
  gen = gen()
  return next(gen.next())
  function next ({done, value}) {
    return new Promise(resolve => {
     if (done) { // finish
       resolve(value)
     } else { // not yet
       value.then(data => {
         next(gen.next(data)).then(resolve)
       })
     }
   })
  }
}
function getRandom () {
  return new Promise(resolve => {
    setTimeout(_ => resolve(Math.random() * 10 | 0), 1000)
  })
}
function * main () {
  let num1 = yield getRandom()
  let num2 = yield getRandom()
 
  return num1 + num2
}
run(main).then(data => {
  console.log(`got data: ${data}`);
})

写法二

递归方程 f(终究结果) = f(之前一切的结果)+f(末了一步的结果)

//同步体式格局写异步
function asyncRun(resf, fn, value) {
    let a = fn(value);
    go(value);
    function go(value) {
        let next = a.next(value);
        if (!next.done) {
            if (next.value instanceof Promise) {
                next.value.then((res) => {
                    go(res);
                });
            } else {
                return go(next.value);
            }
        } else {
            resf(next.value);
        }
    }
}
function* asyncFn(value) {
    let a = yield promiseOne(value);
    let b = yield promiseTwo(a);
    return a + b;
}
function show(item) {
    console.log(item)
}
asyncRun(show, asyncFn, 12);
function promiseOne(xxx) {
    return new Promise((res, rej) => {
        res(xxx + 1);
    })
}
function promiseTwo(xxx) {
    return new Promise((res, rej) => {
        res(xxx + 1);
    })
}

更简朴的要领 async/await

上面庞杂的代码假如变成async/await要怎样做呢

很简朴

// function* asyncFn(value) {
//     let a = yield promiseOne(value);
//     let b = yield promiseTwo(a);
//     return a + b;
// }
function promiseOne(xxx) {
    return new Promise((res, rej) => {
        res(xxx + 1);
    })
}
function promiseTwo(xxx) {
    return new Promise((res, rej) => {
        res(xxx + 1);
    })
}
async function asyncFn(value) {
    let a = await promiseOne(value);
    let b = await promiseTwo(a);
    return a + b;
}
asyncFn(12).then((res)=>{
    console.log(res)
});

经由过程上面的例子,我们能够发明实在async/await本质上实际上是 generator的一个语法糖

await就是yield , async 的作用就是将函数编程语法糖

假如背的话很简答两条划定规矩:

  1. await背面必需是promise函数
  2. async 标记过得函数实行后返回的promise

经由过程这类要领就能够简朴的完成异步了

    原文作者:kyssion
    原文地址: https://segmentfault.com/a/1190000019345262
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞