异步编程(回调函数,promise)
一、回调函数
①概念:一般情况下,程序会时常通过API调用库里所预先备好的函数。但是有些库函数却要求应用先传给它一个函数,好在合适的时候调用,以完成目标任务。这个被传入的、后又被调用的函数就称为回调函数(callback function),也就是一个函数作为另外一个函数的参数使用。如果需要得到一个函数内部的异步操作的结果,这时候必须通过回调函数来获取。
②推导:
③数组遍历中使用的回调函数
- every() 方法测试数组的所有元素是否都通过了指定函数的测试
function isBelowThreshold(currentValue) { return currentValue < 40; } var array1 = [1, 30, 39, 29, 10, 13]; console.log(array1.every(isBelowThreshold));// true
- forEach() 方法对数组的每个元素执行一次提供的函数
var array1 = ['a', 'b', 'c']; array1.forEach(function(element) { console.log(element); }); // expected output: "a" // expected output: "b" // expected output: "c"
- some() 方法测试数组中的某些元素是否通过由提供的函数实现的测试
var array = [1, 2, 3, 4, 5]; var even = function(element) { // checks whether an element is even return element % 2 === 0; }; console.log(array.some(even)); // expected output: true
- includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false
var array1 = [1, 2, 3]; console.log(array1.includes(2)); // expected output: true
- map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果
var array1 = [1, 4, 9, 16]; // pass a function to map const map1 = array1.map(x => x * 2); console.log(map1); // expected output: Array [2, 8, 18, 32]
- reduce() 方法对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值。
const array1 = [1, 2, 3, 4]; const reducer = (accumulator, currentValue) => accumulator + currentValue; console.log(array1.reduce(reducer)); // 1 + 2 + 3 + 4 // expected output: 10 console.log(array1.reduce(reducer, 5)); // 5 + 1 + 2 + 3 + 4 // expected output: 15
④ajax请求里使用回调函数
function get(url,callback){ var oReq=new XMLHttpRequest() // 当请求加载成功以后要调用指定的函数 oReq.onload=function(){ // 现在需要得到这里的oReq.oReq.responseText callback(oReq.responseText) } oReq.open('get',url,true) oReq.send() } get('data.json',function(data){ console.log(data) })
⑤ES6的find和findindex方法
// find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。 var array1 = [5, 12, 8, 130, 44]; var found = array1.find(function(element) { return element > 10; }); console.log(found); // expected output: 12
// findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。 var array1 = [5, 12, 8, 130, 44]; function findFirstLargeNumber(element) { return element > 13; } console.log(array1.findIndex(findFirstLargeNumber)); // expected output: 3
// 原理 var users=[ {id:1,name:'曹操'}, {id:2,name:'许褚'}, {id:3,name:'典韦'}, {id:4,name:'于禁'}, ] Array.prototype.myFind=function(conditionFunc){ for(var i=0;i<this.length;i++){ if(conditionFunc(this[i],i)){ return this[i] } } } var ret=users.myFind(function(item,index){ return item.id===4 }); console.log(ret);//{ id: 4, name: '于禁' }
二、promise
①无法保证顺序的代码
var fs=require('fs'); // 读取a.txt(里面包括文本 aaa)文件 fs.readFile('./a.txt','utf8',function(err,data){ if(err){ // 抛出异常:阻止程序的执行,把错误信息打印到控制台 throw err } console.log(data) }) // 读取b.txt(里面包括文本 bbb)文件b fs.readFile('./b.txt','utf8',function(err,data){ if(err){ // 抛出异常:阻止程序的执行,把错误信息打印到控制台 throw err } console.log(data) }) // 读取c.txt(里面包括文本 ccc)文件 fs.readFile('./c.txt','utf8',function(err,data){ if(err){ // 抛出异常:阻止程序的执行,把错误信息打印到控制台 throw err } console.log(data) })
②通过回调嵌套的方式来保证顺序
var fs=require('fs'); // 读取a.txt(里面包括文本 aaa)文件 fs.readFile('./a.txt','utf8',function(err,data){ if(err){ // 抛出异常:阻止程序的执行,把错误信息打印到控制台 throw err } console.log(data); // 读取b.txt(里面包括文本 bbb)文件b fs.readFile('./b.txt','utf8',function(err,data){ if(err){ // 抛出异常:阻止程序的执行,把错误信息打印到控制台 throw err } console.log(data); // 读取c.txt(里面包括文本 ccc)文件 fs.readFile('./c.txt','utf8',function(err,data){ if(err){ // 抛出异常:阻止程序的执行,把错误信息打印到控制台 throw err } console.log(data) }); }); });
③为了解决回调嵌套编码方式带来的问题(代码不美观,并且不好维护),ecmascript 6里增加了一个API:promise
- promise代码执行顺序
var fs=require('fs'); console.log(1) // 创建promise容器:一旦建立,就开始执行里面的代码 new Promise(function(){ console.log(2); fs.readFile('./a.txt','utf8',function(err,data){ if(err){ throw err; } console.log(3); console.log(data) }); }); console.log(4) //执行顺序是:1 2 4 3 aaa
- promise图示
- promise基本语法
var fs=require('fs'); // 创建promise容器:一旦建立,就开始执行里面的代码 var p1=new Promise(function(resolve,reject){ fs.readFile('./aa.txt','utf8',function(err,data){ if(err){ // promise容器中的任务失败,pending状态变为rejectd reject(err) }else{ // promise容器中的任务成功,pending状态变为resolved // 这里调用的resolve方法就是下面then方法传递的第一个参数function(data){} resolve(data) } }); }); // 当p1成功以后,执行then方法的function(data){} // 当p1失败以后,执行then方法的function(err){} p1.then(function(data){ console.log(data) },function(err){ console.log('文件读取失败',err) })
- 解决读取多个文件的嵌套问题
var fs=require('fs'); // 读取a.txt(里面包括文本 aaa)文件 var p1=new Promise(function(resolve,reject){ fs.readFile('./a.txt','utf8',function(err,data){ if(err){ reject(err) }else{ resolve(data) } }); }); // 读取b.txt(里面包括文本 bbb)文件 var p2=new Promise(function(resolve,reject){ fs.readFile('./b.txt','utf8',function(err,data){ if(err){ reject(err) }else{ resolve(data) } }); }); // 读取c.txt(里面包括文本 ccc)文件 var p3=new Promise(function(resolve,reject){ fs.readFile('./c.txt','utf8',function(err,data){ if(err){ reject(err) }else{ resolve(data) } }); }); // then处理:当return一个promise对象,后续的then方法中的第一个参数就会接收这个对象 p1.then(function(data){ console.log(data); return p2//后续的then里面的第一个参数会作为p2的resolve },function(err){ console.log('文件读取失败',err) }).then(function(data){ console.log(data); return p3//后续的then里面的第一个参数会作为p3的resolve },function(err){ console.log('文件读取失败',err) }).then(function(data){ console.log(data); console.log('end') },function(err){ console.log('文件读取失败',err) })
-
封装promise版本的readFile方法
var fs=require('fs'); // 读取文件封装函数 function pReadFile(filePath){ return new Promise(function(resolve,reject){ fs.readFile(filePath,'utf8',function(err,data){ if(err){ reject(err) }else{ resolve(data) } }); }) } pReadFile('./a.txt').then(function(data){ console.log(data); return pReadFile('./b.txt') }).then(function(data){ console.log(data); return pReadFile('./c.txt') }).then(function(data){ console.log(data); })
⑤promise使用场景:数据来源于多个数据接口,形成的嵌套问题
⑥mongoose所有的API都支持promise
⑧参考文章:http://es6.ruanyifeng.com/#docs/promise
【转载文章务必保留出处和署名,谢谢!】