异步编程解决方案

  • 事件发布/订阅模式
  • promise/deferrd模式
  • 流程控制模式

事件发布/订阅模式

事件监听器模式是异步回调的事件化,又称发布订阅/模式

node核心模块events

方法

  • addListener/on
  • once
  • removeListener
  • removeAllListeners
  • emit

简单操作

let events = require(‘events‘)
let request = require(‘http‘).request
let emit = new events.EventEmitter()
emit.on(‘message‘, function (msg) {
    console.log(msg)
})
emit.once(‘message1‘, function (msg) {
    console.log(msg)
})
emit.emit(‘message‘,‘message‘)
emit.emit(‘message‘,‘message‘)
emit.emit(‘message1‘,‘message1‘)
emit.emit(‘message1‘,‘message1‘)

此示例中,因为message事件通过on来监听,message1通过once来监听,所以message事件的回调可以执行多次,message1事件的回调只能执行一次

结果:

message
message
message1

事件分发/订阅也可以用来做一个功能黑盒,分发者只需要触发这个事件,不需要知道有多少订阅者,这些订阅者都在处理什么业务。事件侦听器模式也是一种钩子机制,node中的封装很多是一个黑盒,不使用事件监听我们很难知道内部状态变化的中间数据。使用事件侦听器可以不必知道内部如何实现,只关注特定的事件点,比如下面的http请求

let request = require(‘http‘).request
let options = {
    host: ‘www.google.com‘,
    port: 80,
    path: ‘/upload‘,
    method: ‘POST‘
}
let req = request(options, function(res) {
    console.log(‘STATUS‘, res.statusCode)
    console.log(‘header‘, JSON.stringify(res.headers))
    res.setEncoding(‘utf-8‘)
    res.on(‘data‘, function (chunck) {
        console.log(‘Chunck‘, chunck)
    })
    res.on(‘end‘, function() {
        console.log(‘end‘)
    })
})
req.on(‘error‘, function(err) {
    console.log(err)
})
req.write(‘data\n‘)
req.write(‘data\n‘)
req.end()

只需要监听data  end  error事件就可以完成功能,不需要知道request究竟如何实现

细节:

  1. 如果对一个事件设置了10个以上的监听器,会有警告。可通过emitter.setMaxListeners(0)将限制去掉
  2. 异常处理,如果有监听error事件,则会执行error中的代码否则将会抛出错误,整个进程结束

利用队列解决雪崩问题

雪崩问题:在高并发高访问量的情况下缓存失效的情境下,大量请求同时发向数据库,数据库无法承受如此多的查询请求,进而影响网站整体性能

var proxy = new events.EventEmitter();
var status = "ready";
var select = function (callback) {
  proxy.once("selected", callback);
  if (status === "ready") {
    status = "pending";
    db.select("SQL", function (results) {
      proxy.emit("selected", results);
      status = "ready";
    });
  }
};

短时间内,如果执行多个select,不会执行多次查询请求,而是会用此次的callback来监听select事件,只执行一次查询请求,当这个请求返回时,触发select事件,执行之前的所有callback,由于用的once来监听,所以每个callback只会执行一次。

多异步之间的协作方案

const fs = require(‘fs‘)
const after = function (times = 0, callback) {
    let time = 0,datas = {}
    return function (key, value) {
        datas[key] = value
        if(++ time >= times) {
            return callback(datas)
        }
    }
}
const done = after(2, function(data) {
    console.log(data)
})
const readFile = function(path,key, callback) {
    fs.readFile(path, ‘utf-8‘, function(err, data) {
        if(err) {
            throw(err)
        }
        callback(key,data)
    })
}
readFile(‘../a.json‘, ‘a‘, done)
readFile(‘../b.json‘, ‘b‘, done)

通过总数和当前执行的次数来计数并执行最终的calback

const done = after(2, function(data) {
    console.log(data)
})
let emit = new events.EventEmitter()
emit.on(‘done‘, done)
emit.on(‘done‘, other)
function readFile(path, key, emit, name) {
    fs.readFile(path, ‘utf-8‘, function(err, data) {
        if(err) {
            throw err
        }
        emit.emit(name, key, data)
    })
}
readFile(‘../a.json‘, ‘a‘, emit, ‘done‘)
readFile(‘../b.json‘, ‘b‘, emit, ‘done‘)

通过事件监听来实现多对多异步

let EventProxy = require(‘eventproxy‘)
let fs = require(‘fs‘)
let proxy = new EventProxy()
proxy.all(‘a‘, ‘b‘, function(a, b) {
    console.log(‘a‘,a,‘b‘,b)
})
function readFile(path, name) {
    fs.readFile(path, ‘utf-8‘, function(err, data) {
        if(err) {
            throw err
        }
        proxy.emit(name, data)
    })
}
readFile(‘../a.json‘, ‘a‘)
readFile(‘../b.json‘, ‘b‘)
readFile(‘../a.json‘, ‘a‘)
readFile(‘../b.json‘, ‘b‘)

通过eventproxy来实现一对多异步

promise/deffered模式

用event实现一个request的promise

var Deferred = function() {
    this.promise = new Promise()
    this.status = ‘unfullfiled‘
}
Deferred.prototype.resolve = function(obj) {
    this.status = ‘fullfiled‘
    this.promise.emit(‘success‘, obj)
}
Deferred.prototype.reject = function(err) {
    this.status = ‘failed‘
    this.promise.emit(‘error‘, err)
}
Deferred.prototype.process = function(data) {
    this.promise.emit(‘process‘, data)
}

var promisify = function(res) {
    var deffer = new Deferred()
    let result = ‘‘
    res.setEncoding(‘utf-8‘)
    res.on(‘data‘, function(chunck) {
        result += chunck
        deffer.process(chunck)
    })
    res.on(‘end‘, function() {
        deffer.resolve(result)
    })
    res.on(‘error‘, function(err) {
        deffer.reject(err)
    })
    return deffer.promise
}

let option = {
    host: ‘www.baidu.com‘,
    port: 80,
    method: ‘POST‘,
    path: ‘upload‘
}
let req = http.request(option, function(res) {
    promisify(res).then(function(data) {
        console.log(‘success‘, data)
    }, function(err) {
        console.log(‘error‘, err)
    }, function(chunck) {
        console.log(‘Chunck‘, chunck)
    })
})
req.write(‘data‘)
req.write(‘data‘)
req.end()

defferd为内部封装promise为外部调用

有一些库来帮助定制化promise

流程控制库

  1. 尾触发和next(connect)
  2. 流程控制模块async
    1. 无依赖串行

      let async = require(‘async‘)
      let fs = require(‘fs‘)
      async.series([
          function(callback) {
              fs.readFile(‘../a.json‘, ‘utf-8‘, callback)
          },
          function(callback) {
              fs.readFile(‘../b.json‘, ‘utf-8‘, callback)
          },
      ], function(err, datas) {
          console.log(datas)
      })

      相当于

      let fs = require(‘fs‘)
      let callback = function(err, results) {
          console.log(results)
      }
      fs.readFile(‘../a.json‘, ‘utf-8‘, function(err, content) {
          if(err) {
              return callback(err)
          }
          fs.readFile(‘../b.json‘, ‘utf-8‘, function(err, data) {
              if(err) {
                  return callback(err)
              }
              callback(null, [content, data])
          })
      })

      callback传递由async完成

    2. 并行
      let fs = require(‘fs‘)
      let async = require(‘async‘)
      async.parallel([
          function(callback) {
              fs.readFile(‘../a.json‘, ‘utf-8‘, callback)
          },
          function(callback) {
              fs.readFile(‘../b.json‘, ‘utf-8‘, callback)
          },
      ], function(err, results) {
          console.log(results)
      })

      相当于

      let fs = require(‘fs‘)
      
      let counter = 2
      let results = []
      let failError = null
      let callback = function(err, data) {
          console.log(data)
      }
      
      let done = function(index, data) {
          results[index] = data
          if(-- counter === 0) {
              callback(null, results)
          }
      }
      
      let fail = function(err) {
          if(!failError) {
              failError = err
              callback(failError)
          }
      }
      
      fs.readFile(‘../a.json‘, ‘utf-8‘, function(err, data) {
          if(err) {
              fail()
          }
          done(0, data)
      })
      fs.readFile(‘../b.json‘, ‘utf-8‘, function(err, data) {
          if(err) {
              fail()
          }
          done(1, data)
      })
    3. 前一个输出为后一个输入async.waterfall
    4. 自动化执行
      let async = require(‘async‘)
      let fs = require(‘fs‘)
      
      let dep = {
          b: function(callback) {
              fs.readFile(‘../b.json‘, ‘utf-8‘, function(err, data) {
                  console.log(data)
                  callback()
              })
          },
          a: [‘b‘, function(callback) {
              fs.readFile(‘../a.json‘, ‘utf-8‘, function(err, data) {
                  console.log(data)
              })
          }]
      }
      
      async.auto(dep)

      使a在b执行完后执行

原文地址:https://www.cnblogs.com/ranjianxi/p/8404541.html

时间: 2024-08-03 19:25:39

异步编程解决方案的相关文章

异步编程解决方案之事件发布/订阅模式

时间监听模式是广泛用于异步编程的模式,是回调函数的事件化,又称不发订阅模式. nodejs的events模块就是发布订阅模式的一个简单实现,不存在preventDefault,stopPropagation,stopImmediatePropagation,等控制事件传递的方法. 它具有addListner/on(),once(),removeListner(),removeAllLisetner()和emit等基础监听事件方法. 事件发布/订阅十分简单,如下: //订阅 emitter.on(

nodejs学习笔记 —— 异步编程解决方案

在js或者node编程中,由于异步的频繁和广度使用,使得回调和嵌套的深度导致编程的体验遇到一些挑战,如果写出优雅和好看的代码,本文主要针对异步编程的主流方案做一些总结 1.事件发布/订阅模式 事件监听器模式是一种广泛用于异步编程的模式, 是回调函数的事件化,又称发布/订阅模式, node自身提供events模块,是该模式的一个简单实现. EventPorxy 2.promise/deferrd模式 在2009年被Kris Zyp抽象为一个提议草案,发布在CommonJS规范中, 目前,Commo

javascript的异步编程解决方案收集

缘起 没理解js异步的同学看下面的例子: for (var i = 0; i < 5; i++) { //模拟一个异步操作 setTimeout(() => { console.log(i); }, 1000); } 我们想要的结果是:0,1,2,3,4 结果却出乎意料:5,5,5,5,5 分析 js的特点就是单线程异步非堵塞.需要好好理解这句话:js对于异步操作,不会停下来等待上一个异步操作完成,才进行下一个异步操作. 如果要达到顺序执行,只能用回调:也就是上一个异步操作完成时,再调用下一个

ES6 异步编程解决方案 之 Async

一.async 函数的基本用法 async 函数返回一个 Promise 对象,可以使用 then .catch 方法 添加回调函数 async 函数执行时,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句 [异步函数 同步执行] async 函数有很多种形式: // 函数声明 async function foo() {} // 函数表达式 const foo = async function () {}; // 箭头函数 const foo = async (

异步编程解决方案之Promise/Deferred

Promise三种状态:未完成.完成态.失败态 var events = require('events'); var util = require('util'); var EventEmitter = events.EventEmitter; var Promise = function () { EventEmitter.call(this); } util.inherits(Promise, EventEmitter); Promise.prototype.then = function

JavaScript学习--Item27 异步编程异常解决方案

1.JavaScript异步编程的两个核心难点 异步I/O.事件驱动使得单线程的JavaScript得以在不阻塞UI的情况下执行网络.文件访问功能,且使之在后端实现了较高的性能.然而异步风格也引来了一些麻烦,其中比较核心的问题是: 1.函数嵌套过深 JavaScript的异步调用基于回调函数,当多个异步事务多级依赖时,回调函数会形成多级的嵌套,代码变成 金字塔型结构.这不仅使得代码变难看难懂,更使得调试.重构的过程充满风险. 2.异常处理 回调嵌套不仅仅是使代码变得杂乱,也使得错误处理更复杂.这

C# 异步编程1 APM模式异步程序开发

C#已有10多年历史,单从微软2年一版的更新进度来看活力异常旺盛,C#中的异步编程也经历了多个版本的演化,从今天起着手写一个系列博文,记录一下C#中的异步编程的发展历程.广告一下:喜欢我文章的朋友,请点下面的“关注我”.谢谢 我是2004年接触并使用C#的,那时C#版本为1.1,所以我们就从就那个时候谈起.那时后在大学里自己看书写程序,所写的程序大都是同步程序,最多启动个线程........其实在C#1.1的时代已有完整的异步编程解决方案,那就是APM(异步编程模型).如果还有不了解“同步程序.

深入浅出NodeJS——异步编程

函数式编程 Javascript中函数作为一等公民,函数看成普通对象,可以作为参数或者返回值. 高阶函数:函数作为参数或者将函数作为返回值的函数 异步编程优势 基于事件驱动的非阻塞I/O模型 异步编程难点 (1) 异常处理,通常try/catch不一定适用,因为callback并不是在当前Tick中执行. Node在异常处理中约定将异常作为回调函数的第一个实参传回. (2)  函数嵌套过深 (3) 代码阻塞:没有sleep函数,通过setInterval()和setTimeout()模拟 (4)

使用任务Task 简化异步编程

使用任务简化异步编程 Igor Ostrovsky 下载代码示例 异步编程是实现与程序其余部分并发运行的较大开销操作的一组技术. 常出现异步编程的一个领域是有图形化 UI 的程序环境:当开销较大的操作完成时,冻结 UI 通常是不可接受的. 此外,异步操作对于需要并发处理多个客户端请求的服务器应用程序来说非常重要. 在实践过程中出现的异步操作的典型例子包括向服务器发送请求并等待响应.从硬盘读取数据以及运行拼写检查等开销较大的计算. 以一个含 UI 的应用程序为例. 该应用程序可以使用 Window