所谓异步就是指给定了一串函数调用a,b,c,d,各个函数的执行完结返回过程并不顺序为a->b->c->d(这属于传统的同步编程模式),a,b,c,d调用返回的时机并不肯定。javascript
对于js代码来讲,这种异步编程尤为重要并大量存在,很大的缘由是js为单线程模式,其中包含了ui刷新和响应。想像一下,若是咱们写了一段js代码,他会发起ajax请求,若是为同步模式,就意味着这时js单线程一直等待这个ajax完成,这期间是不能响应任何用户交互的,这种体验是不可接受的。如今随着服务端js-nodejs的兴起,其超强的异步模式更为nodejs相较于php,java等成熟后端语言的卖点。全部和IO相关的都应该设计成异步模式(好比磁盘IO,网络请求等)php
实现异步编程模式能够有如下方式:java
1. 回调callback,node
看如下nodejs代码:须要注意的是,即便在服务端的nodejs环境中,其运行模型也是单线程模型!http://www.javashuo.com/article/p-kjgpbgsk-bk.htmlc++
// 遍历目录,找出最大的一个文件 // 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 调试的过程:web
$ 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主线程的运行周期的适当时机来调用ajax
2. promise编程
promise表明了一个异步操做最终的结果,它是一个对象,表明着延迟计算(deferred computation)的最终结果(除了延迟计算,更多的是一个异步的IO操做). promise也是一种状态机,它有三个不一样的状态:pending, fulfilled,rejected.一旦promise完成(fulfilled,或者rejected),它就不能再被变动状态。json
when a promise is ready, its .then/catch/finally
handlers are put into the queuec#
当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 的实现机制: http://www.javashuo.com/article/p-xzyfgegj-ma.html
Promise.resolve schedule a microtask and the setTimeout schedule a macrotask. And the microtasks are executed before running the next macrotask
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); })
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中获取