JavaScript基礎——回調(callback)是什麼

上篇文章《JavaScript基礎——你真的瞭解JavaScript嗎?》,咱們明白了JavaScript是一個單線程、非阻塞、異步、解釋性語言,清楚了什麼是單線程、進程、阻塞、調用堆棧、異步回調、任務循環等感念,沒看的或者不清楚的建議點擊《JavaScript基礎——你真的瞭解JavaScript嗎?》再看一遍,只有理解了,才能輕鬆閱讀理解本篇文章內容。前端

什麼是callback?

JavaScript 是單線程工做,這意味着兩段腳本不能同時運行,而是必須一個接一個地運行。咱們人類是多線程工做。您可使用多個手指打字,能夠一邊開車一邊與人交談。惟一一個會妨礙咱們的是打噴嚏,由於當咱們打噴嚏的時候,全部當前進行的活動都必須暫停。這真是很是討厭,尤爲是當您在開車並想與人交談時。您可不想編寫像打噴嚏似的代碼。JavaScript因爲單線程限制,防止阻塞,只能經過異步函數的調用方式,把須要延遲處理的事件放入事件循環隊列。到目前爲止,回調是編寫和處理JavaScript程序異步邏輯的最經常使用方式。說了這麼多,既然回調這麼重要,到底什麼是回調(callback)呢?數據庫

簡單的定義:回調就是一個在另一個函數執行完後要執行的函數
複雜的定義:在JavaScript中,函數是對象。所以函數能夠將函數做爲參數,而且能夠由其餘函數進行返回。執行此操做的函數稱爲高階函數。任何做爲參數傳遞的函數都稱爲回調函數。
爲何須要回調?
開篇已經介紹了JavaScript是單線程的,須要通回調函數處理異步相關的邏輯,理論仍是過於生硬,咱們仍是來看段代碼吧:

function first(){
console.log(1);
}
function second(){
console.log(2);
}
first();
second();複製代碼
正如你所料,先執行first函數,再執行second函數,控制檯將輸出如下內容:

1
2複製代碼
目前看來沒什麼問題,若是first()函數中含有某種沒法當即執行的函數呢?例如,咱們必須發送請求而後等待結果響應的API請求?爲了模擬API請求,咱們可使用setTimeout函數模擬。咱們將函數延遲500毫秒來模擬請求,咱們更改後代碼以下:

function first(){
// Simulate a code delay
setTimeout( function(){
console.log(1);
}, 500 );
}
function second(){
console.log(2);
}
first();
second();複製代碼
咱們將 console.log(1) 延遲500毫秒輸出,這段代碼會怎麼輸出呢?

2
1複製代碼
咱們但願的順序先執行first,再執行second,可是因爲JavaScript是異步的,全部的延遲處理都要放入循環隊列裏,所以事與願違,不能按照咱們的但願順序輸出。若是但願這段代碼按照咱們的意願輸出,咱們可使用回調函數,確保某些代碼執行完了,在循序執行另一段代碼。
建立回調
說了這麼多,讓咱們建立一個簡單的回調!
咱們打開編輯器,先輸入以下代碼:

function doHomework(subject) {
alert(`Starting my ${subject} homework.`);
}複製代碼
上面咱們建立了doHomeWork的函數,咱們接受一個變量,經過控制檯調用,將獲得下面的提示:

doHomework('math');
// Alerts: Starting my math homework.複製代碼
接着,咱們開始添加回調,在doHomework函數中添加一個參數callback,而後在第二個參數中回調咱們定義的函數。代碼以下:

function doHomework(subject, callback) {
alert(`Starting my ${subject} homework.`);
callback();
}
doHomework('math', function() {
alert('Finished my homework');
});
複製代碼

正如你但願的,咱們在控制檯裏運行上述代碼,將會受到兩個連續的alert,Starting my math homework,而後彈出 Finished my homework。編程

可是回調函數並非非得在調用函數中定義,咱們能夠單獨定義,修改後的代碼以下:json

function doHomework(subject, callback) {
alert(`Starting my ${subject} homework.`);
callback();
}
function alertFinished(){
alert('Finished my homework');
}
doHomework('math', alertFinished);

複製代碼

此示例的輸出結果和上段代碼的結果一致,咱們實現了在doHomework函數中調用alertFinished,實現了函數做爲參數進行傳遞,實現了回調函數的建立。
bash

用回調寫一段真實業務場景的代碼!

例如咱們有一個需求,用NodeJs實現從論壇帖子列表中顯示其中的一個帖子的信息及留言列表信息,代碼以下:微信

DB/posts.json(帖子列表數據)

[
{
"id": "001",
"title": "Greeting",
"text": "Hello World",
"author": "Jane Doe"
},
{
"id": "002",
"title": "JavaScript 101",
"text": "The fundamentals of programming.",
"author": "Alberta Williams"
},
{
"id": "003",
"title": "Async Programming",
"text": "Callbacks, Promises and Async/Await.",
"author": "Alberta Williams"
}
]複製代碼

DB/comments.json(評論列表)

[
{
"id": "phx732",
"postId": "003",
"text": "I don't get this callback stuff."
},
{
"id": "avj9438",
"postId": "003",
"text": "This is really useful info."
},
{
"id": "gnk368",
"postId": "001",
"text": "This is a test comment."
}
]複製代碼

Index.js

const fs = require('fs');
const path = require('path');
const postsUrl = path.join(__dirname, 'db/posts.json');
const commentsUrl = path.join(__dirname, 'db/comments.json');
//return the data from our file
function loadCollection(url, callback) {
fs.readFile(url, 'utf8', function(error, data) {
if (error) {
console.log(error);
} else {
return callback(JSON.parse(data));
}
});
}
//return an object by id
function getRecord(collection, id, callback) {
var collectobj=collection.find(function(element){
return element.id == id;
});
callback(collectobj);
return collectobj;
}
//return an array of comments for a post
function getCommentsByPost(comments, postId) {
return comments.filter(function(comment){
return comment.postId == postId;
});
}
loadCollection(postsUrl, function(posts){
loadCollection(commentsUrl, function(comments){
getRecord(posts, "001", function(post){
const postComments = getCommentsByPost(comments, post.id);
console.log(post);
console.log(postComments);
});
});
});

複製代碼

你們請注意,咱們在loadCollection函數中咱們沒有使用try/catch,使用的是if/else,由於catch沒法從readFile方法中獲取錯誤。上述代碼還須要完善,我沒有包含任何錯誤處理。若是在任何步驟中發生錯誤,程序將沒法繼續。
多線程

錯誤處理是很重要的事情,咱們寫代碼時要認證對待,要嚴格對待,好比咱們要編寫一個用戶登陸的功能。涉及從網頁表單裏獲取用戶名和密碼,查詢咱們的數據庫,確認用戶信息是否正確,驗證經過後,將用戶引導到用戶中心頁面。若是用戶名密碼格式不正確,用戶名密碼不正確,咱們應該將錯誤信息返回給用戶,並引導用戶從新登陸。異步

總結

很好!咱們一塊兒把回調的內容學完了,理解了什麼是回調,異步編程是咱們的代碼中使用的一種方法,用於推遲事件以便之後執行。當您處理異步任務時,回調是一種解決方案,以便它們按順序執行。編輯器

若是咱們有多個任務依賴於前幾個任務的結果,那咱們就要使用多個嵌套回調,可是就會引起「回調地域」(過多的回調嵌套會使得代碼變得難以理解與維護),還好Promise解決了「回調地獄」的問題,讓咱們以同步的方式編寫代碼,小編將會再下篇文章裏進行詳細介紹,敬請期待!異步編程

更多精彩內容,請微信關注」前端達人」公衆號!

相關文章
相關標籤/搜索