• javascript异步编程学习及实例


    所谓异步就是指给定了一串函数调用a,b,c,d,各个函数的执行完结返回过程并不顺序为a->b->c->d(这属于传统的同步编程模式),a,b,c,d调用返回的时机并不确定。

    对于js代码来说,这种异步编程尤其重要并大量存在,很大的原因是js为单线程模式,其中包含了ui刷新和响应。想像一下,如果我们写了一段js代码,他会发起ajax请求,如果为同步模式,就意味着这时js单线程一直等待这个ajax完成,这期间是不能响应任何用户交互的,这种体验是不可接受的。现在随着服务端js-nodejs的兴起,其超强的异步模式更为nodejs相较于php,java等成熟后端语言的卖点。所有和IO相关的都应该设计成异步模式(比如磁盘IO,网络请求等)

    实现异步编程模式可以有以下方式:

    1. 回调callback,

    看以下nodejs代码:需要注意的是,即使在服务端的nodejs环境中,其运行模型也是单线程模型https://blog.csdn.net/j2IaYU7Y/article/details/81623516

    // 遍历目录,找出最大的一个文件
    // nodejs的readdir为一个典型的异步调用过程,函数调用马上返回,但是结果却等到目录扫描完成后,调用回调函数来通知应用去处理
    
    const fs = require('fs');
    const path = require('path');
    
    function findLargest(dir, callback) {
        fs.readdir(dir, function (err, files) {
            if (err) return callback(err); // [1]
            let count = files.length; // [2]
            let errored = false;
            let stats = [];
            files.forEach( file => {
                fs.stat(path.join(dir, file), (err, stat) => {
                    if (errored) return; // [1]
                    if (err) {
                        errored = true;
                        return callback(err);
                    }
                    stats.push(stat); // [2]
    
                    if (--count === 0) {
                        let largest = stats
                            .filter(function (stat) { return stat.isFile(); })
                            .reduce(function (prev, next) {
                                if (prev.size > next.size) return prev;
                                return next;
                            });
                        callback(null, files[stats.indexOf(largest)]);
                        console.log('readdir finished!')
                    }
                });
            });
        });
        console.log('before readdir callback called!')
    }
    
    findLargest('./', function (err, filename) {
        if (err) return console.error(err);
        console.log('largest file was:', filename);
    });
    // 其执行log如下
    before readdir callback called!
    largest file was: halls-test.js
    readdir finished!

    我们看看通过node --inspect-brk 调试的过程:

    $ node --inspect-brk maxfile.js
    Debugger listening on ws://127.0.0.1:9229/ed354fe8-fdfc-466b-b0ea-fcc7fccb4b36
    For help, see: https://nodejs.org/en/docs/inspector
    Debugger attached.
    before readdir callback called!
    largest file was: halls-test.js
    readdir finished!
    Waiting for the debugger to disconnect...

    callback导致的问题是无法通过try catch截取错误,并且当回调嵌套时流程更加显得复杂,程序可读性差;callback将在fs.readdir的function参数中调用,该callback(本例中实际上是fs.readdir的function参数)将被fs.readdir调用操作真正异步执行完成时(本身函数调用立即返回,而执行通过系统调用异步执行),放入javascript event queue中,底层readdir实际操作(往往是由js引擎c++代码执行)结束后,将有event发生,这时会将该callback function放到event queue中(并包含了对应的readdir返回数据),从而由event loop引擎在js主线程的运行周期的适当时机来调用

    2. promise

    promise代表了一个异步操作最终的结果,它是一个对象,代表着延迟计算(deferred computation)的最终结果(除了延迟计算,更多的是一个异步的IO操作). promise也是一种状态机,它有三个不同的状态:pending, fulfilled,rejected.一旦promise完成(fulfilled,或者rejected),它就不能再被变更状态。

    when a promise is ready, its .then/catch/finally handlers are put into the queue

    当promise resolve/reject时,也就是该promise ready时,会将promise的then定义的handler插入event queue,在下一个event loop周期时,如果主线程没有任务执行了,将被取出执行

    再看看以下代码对应的解读:

    let promise = Promise.resolve();
    
    promise.then(() => alert("promise done"));
    
    alert("code finished"); // this alert shows first

    如果我们希望promise done的打印在code finished打印之前,怎么办?答案是then的链接,每一个then都会返回一个新的promise

    Promise.resolve()
      .then(() => alert("promise done!"))
      .then(() => alert("code finished"))

    promise 的实现机制: https://juejin.im/post/5a30193051882503dc53af3c#heading-14

    Promise.resolve schedule a microtask and the setTimeout schedule a macrotask. And the microtasks are executed before running the next macrotask

    使用XMLHttpRequest实现promise形式的ajax

    function get(url) {
      // Return a new promise.
      return new Promise(function(resolve, reject) {
        // Do the usual XHR stuff
        var req = new XMLHttpRequest();
        req.open('GET', url);
    
        req.onload = function() { // 这里是原生的callback api,也就是当onload事件发生时会被event loop调用,从而通过resolve再push到event queue中,对应then中的handler被下一个loop调用
          // This is called even on 404 etc
          // so check the status
          if (req.status == 200) {
            // Resolve the promise with the response text
            resolve(req.response);
          }
          else {
            // Otherwise reject with the status text
            // which will hopefully be a meaningful error
            reject(Error(req.statusText));
          }
        };
    
        // Handle network errors
        req.onerror = function() {
          reject(Error("Network Error"));
        };
    
        // Make the request
        req.send();
      });
    }

    https://developers.google.com/web/fundamentals/primers/promises?hl=zh-tw

    get('story.json').then(function(response) {
      console.log("Success!", response);
    }, function(error) {
      console.error("Failed!", error);
    })

    3. ES7 async/await关键字

    var sleep = function (time) {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                // 返回 ‘ok’
                resolve('ok');
            }, time);
        })
    };
    var sleepwithReject = function (time) {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                // 返回 ‘ok’
                reject('Error');
            }, time);
        })
    };
    
    var start = async function () {
        let result = await sleep(3000);
        console.log(result); // 收到 ‘ok’
    };
    var reject = async function () {
    try{
        let result = await sleepwithReject(3000);
        console.log(result); // 不会执行,因为被reject了,会触发一个异常
    }catch (err){
        console.log(err);// 这里捕捉到Error
    }
    }
    ;
    start()
    reject()

    基本规则:

    1. async关键字表示这是一个async函数,await关键字只能在这个async关键字指示的函数中;

    2. await表示在这里等待promise执行完成并返回结果,promise完成后才能继续执行

    3. await后面跟着的应该是一个promise对象(当然如果是非promise对象,则只会立即执行)

    4. await紧跟的promise resolve/reject之后其resolve或者reject返回的结果直接在这里可以synchrosely(同步地)返回

    5. 如果发生错误,则可以在try catch中获取

  • 相关阅读:
    配置 PHP 的 Session 存储到 Redis
    ab测试工具
    mysql:general_log 日志、数据库线程查询、数据库慢查询
    upload-labs-env文件上传漏洞 11-19关
    upload-labs-env文件上传漏洞 1-10关
    Webshell免杀绕过waf
    虚拟机安装安全狗apache服务的一些问题解决方式(11.5)
    SQL注入过WAF(11.4 第三十三天)
    内联注入和堆叠注入(10.30 第三十天)
    SQL server 注入 和 SQL server 扩展(10.29 第二十九天)
  • 原文地址:https://www.cnblogs.com/kidsitcn/p/10868600.html
Copyright © 2020-2023  润新知